@turinhub/atomix-common-ui 0.5.0 → 0.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/dist/AuthPanel-CTKx618F.cjs +2 -0
- package/dist/AuthPanel-CTKx618F.cjs.map +1 -0
- package/dist/{AuthPanel-D2HFX8eN.js → AuthPanel-Cn_WwmjX.js} +259 -212
- package/dist/AuthPanel-Cn_WwmjX.js.map +1 -0
- package/dist/PDFSidebar-4DtXqqzN.cjs +2 -0
- package/dist/PDFSidebar-4DtXqqzN.cjs.map +1 -0
- package/dist/PDFSidebar-ClnrF4Br.js +239 -0
- package/dist/PDFSidebar-ClnrF4Br.js.map +1 -0
- package/dist/auth.cjs +1 -1
- package/dist/auth.js +1 -1
- package/dist/components/AuthLoginPanel.d.ts.map +1 -1
- package/dist/components/AuthRegisterPanel.d.ts.map +1 -1
- package/dist/components/AuthVisualCarousel.d.ts +2 -0
- package/dist/components/AuthVisualCarousel.d.ts.map +1 -1
- package/dist/components/DataTable.d.ts.map +1 -1
- package/dist/components/ImageReader.d.ts.map +1 -1
- package/dist/components/MarkdownReader.d.ts.map +1 -1
- package/dist/components/PDFReader.d.ts.map +1 -1
- package/dist/components/PDFSidebar.d.ts.map +1 -1
- package/dist/components/SimplePDFReader.d.ts.map +1 -1
- package/dist/components/TableHeader.d.ts.map +1 -1
- package/dist/components/TablePagination.d.ts +2 -1
- package/dist/components/TablePagination.d.ts.map +1 -1
- package/dist/components/VideoReader.d.ts.map +1 -1
- package/dist/components/ui/switch.d.ts +5 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/data-table.cjs.map +1 -1
- package/dist/data-table.js.map +1 -1
- package/dist/file-upload.cjs +1 -1
- package/dist/file-upload.cjs.map +1 -1
- package/dist/file-upload.js +36 -36
- package/dist/file-upload.js.map +1 -1
- package/dist/image-reader.cjs +1 -1
- package/dist/image-reader.cjs.map +1 -1
- package/dist/image-reader.js +1 -0
- package/dist/image-reader.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/markdown-reader.cjs +1 -1
- package/dist/markdown-reader.cjs.map +1 -1
- package/dist/markdown-reader.js +28 -24
- package/dist/markdown-reader.js.map +1 -1
- package/dist/pdf-reader.cjs +1 -1
- package/dist/pdf-reader.cjs.map +1 -1
- package/dist/pdf-reader.js +169 -120
- package/dist/pdf-reader.js.map +1 -1
- package/dist/pdf-sidebar.cjs +1 -1
- package/dist/pdf-sidebar.js +1 -1
- package/dist/simple-pdf-reader.cjs +1 -1
- package/dist/simple-pdf-reader.cjs.map +1 -1
- package/dist/simple-pdf-reader.js +137 -104
- package/dist/simple-pdf-reader.js.map +1 -1
- package/dist/table-header.cjs +1 -1
- package/dist/table-header.cjs.map +1 -1
- package/dist/table-header.js +42 -34
- package/dist/table-header.js.map +1 -1
- package/dist/table-pagination.cjs +1 -1
- package/dist/table-pagination.cjs.map +1 -1
- package/dist/table-pagination.js +49 -43
- package/dist/table-pagination.js.map +1 -1
- package/dist/types/component-types.d.ts +1 -0
- package/dist/types/component-types.d.ts.map +1 -1
- package/dist/video-reader.cjs.map +1 -1
- package/dist/video-reader.js.map +1 -1
- package/package.json +2 -1
- package/dist/AuthPanel-C_2JBE7t.cjs +0 -2
- package/dist/AuthPanel-C_2JBE7t.cjs.map +0 -1
- package/dist/AuthPanel-D2HFX8eN.js.map +0 -1
- package/dist/PDFSidebar-BBtucLK6.js +0 -232
- package/dist/PDFSidebar-BBtucLK6.js.map +0 -1
- package/dist/PDFSidebar-Di0D-yPS.cjs +0 -2
- package/dist/PDFSidebar-Di0D-yPS.cjs.map +0 -1
package/dist/file-upload.js
CHANGED
|
@@ -3,23 +3,23 @@ import { UploadCloud as fs, FileText as hs, X as ps, Loader2 as gs, CheckCircle2
|
|
|
3
3
|
import { useState as V, useRef as vs, useEffect as Ns } from "react";
|
|
4
4
|
import { c as R } from "./utils-B6yFEsav.js";
|
|
5
5
|
const bs = (r) => r < 1024 ? `${r} B` : r < 1024 * 1024 ? `${(r / 1024).toFixed(1)} KB` : r < 1024 * 1024 * 1024 ? `${(r / 1024 / 1024).toFixed(1)} MB` : `${(r / 1024 / 1024 / 1024).toFixed(1)} GB`, q = (r) => `${r.name}-${r.size}-${r.lastModified}-${Math.random().toString(36).slice(2)}`, ws = (r) => {
|
|
6
|
-
const
|
|
7
|
-
return
|
|
8
|
-
}, ks = (r,
|
|
9
|
-
if (!
|
|
6
|
+
const c = r.lastIndexOf(".");
|
|
7
|
+
return c > -1 ? r.slice(c).toLowerCase() : "";
|
|
8
|
+
}, ks = (r, c) => {
|
|
9
|
+
if (!c) return !0;
|
|
10
10
|
const u = r.type.toLowerCase(), j = ws(r.name);
|
|
11
|
-
return
|
|
12
|
-
}, Cs = (r,
|
|
11
|
+
return c.split(",").map((d) => d.trim().toLowerCase()).filter(Boolean).some((d) => d.startsWith(".") ? j === d : d.endsWith("/*") ? u.startsWith(d.slice(0, -1)) : u === d);
|
|
12
|
+
}, Cs = (r, c) => r instanceof Error && r.message ? r.message : typeof r == "string" && r ? r : c;
|
|
13
13
|
function Is({
|
|
14
14
|
components: r,
|
|
15
|
-
title:
|
|
15
|
+
title: c = "文件上传",
|
|
16
16
|
description: u = "选择文件后开始上传,支持 Tale SDK 的直接上传和预签名上传流程。",
|
|
17
17
|
helperText: j,
|
|
18
18
|
accept: d,
|
|
19
19
|
multiple: N = !1,
|
|
20
20
|
maxFiles: J,
|
|
21
21
|
maxSize: b,
|
|
22
|
-
disabled:
|
|
22
|
+
disabled: o = !1,
|
|
23
23
|
autoUpload: Q = !1,
|
|
24
24
|
showUploadButton: Y,
|
|
25
25
|
showResetButton: w = !0,
|
|
@@ -49,11 +49,11 @@ function Is({
|
|
|
49
49
|
}, [l, y]), !r)
|
|
50
50
|
return /* @__PURE__ */ e.jsx("div", { className: "p-4 text-center text-destructive", children: "错误:请通过 components prop 注入 UI 组件" });
|
|
51
51
|
const {
|
|
52
|
-
Card:
|
|
52
|
+
Card: os,
|
|
53
53
|
CardHeader: O,
|
|
54
54
|
CardTitle: I,
|
|
55
55
|
CardDescription: B,
|
|
56
|
-
CardContent:
|
|
56
|
+
CardContent: cs,
|
|
57
57
|
CardFooter: P,
|
|
58
58
|
Button: f
|
|
59
59
|
} = r, h = (s) => {
|
|
@@ -132,7 +132,7 @@ function Is({
|
|
|
132
132
|
for (const t of s)
|
|
133
133
|
await F(t);
|
|
134
134
|
}, G = (s) => {
|
|
135
|
-
if (!s ||
|
|
135
|
+
if (!s || o) return;
|
|
136
136
|
const t = Array.from(s);
|
|
137
137
|
h((i) => {
|
|
138
138
|
const a = ds(t, i);
|
|
@@ -170,14 +170,14 @@ function Is({
|
|
|
170
170
|
/* @__PURE__ */ e.jsx(ys, { className: "h-3.5 w-3.5" }),
|
|
171
171
|
W
|
|
172
172
|
] }) : /* @__PURE__ */ e.jsx("span", { className: "text-xs text-muted-foreground", children: "待上传" }), H = /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
c && I && /* @__PURE__ */ e.jsx(I, { children: c }),
|
|
174
|
+
c && !I && /* @__PURE__ */ e.jsx("div", { className: "text-lg font-semibold leading-none tracking-tight", children: c }),
|
|
175
175
|
u && B && /* @__PURE__ */ e.jsx(B, { children: u }),
|
|
176
176
|
u && !B && /* @__PURE__ */ e.jsx("div", { className: "text-sm text-muted-foreground", children: u })
|
|
177
177
|
] });
|
|
178
|
-
return /* @__PURE__ */ e.jsxs(
|
|
179
|
-
O && (
|
|
180
|
-
/* @__PURE__ */ e.jsxs(
|
|
178
|
+
return /* @__PURE__ */ e.jsxs(os, { className: R("overflow-hidden", ts), children: [
|
|
179
|
+
O && (c || u) ? /* @__PURE__ */ e.jsx(O, { children: H }) : (c || u) && /* @__PURE__ */ e.jsx("div", { className: "space-y-1.5 p-6", children: H }),
|
|
180
|
+
/* @__PURE__ */ e.jsxs(cs, { className: "space-y-4", children: [
|
|
181
181
|
/* @__PURE__ */ e.jsx(
|
|
182
182
|
"input",
|
|
183
183
|
{
|
|
@@ -187,31 +187,31 @@ function Is({
|
|
|
187
187
|
multiple: N,
|
|
188
188
|
className: "sr-only",
|
|
189
189
|
onChange: (s) => G(s.target.files),
|
|
190
|
-
disabled:
|
|
190
|
+
disabled: o
|
|
191
191
|
}
|
|
192
192
|
),
|
|
193
193
|
/* @__PURE__ */ e.jsxs(
|
|
194
194
|
"div",
|
|
195
195
|
{
|
|
196
196
|
role: "button",
|
|
197
|
-
tabIndex:
|
|
198
|
-
"aria-disabled":
|
|
197
|
+
tabIndex: o ? -1 : 0,
|
|
198
|
+
"aria-disabled": o,
|
|
199
199
|
className: R(
|
|
200
|
-
"flex min-h-40 cursor-pointer flex-col items-center justify-center rounded-lg border border-dashed border-muted-foreground/30 bg-muted/20 px-6 py-8 text-center transition-colors focus-visible:outline-none focus-visible:ring-
|
|
201
|
-
is && "border-primary bg-primary/
|
|
202
|
-
|
|
200
|
+
"flex min-h-40 cursor-pointer touch-manipulation flex-col items-center justify-center rounded-lg border border-dashed border-muted-foreground/30 bg-muted/20 px-6 py-8 text-center transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
201
|
+
is && "border-primary bg-primary/10 shadow-inner",
|
|
202
|
+
o && "cursor-not-allowed opacity-60",
|
|
203
203
|
ns
|
|
204
204
|
),
|
|
205
205
|
onClick: () => {
|
|
206
206
|
var s;
|
|
207
|
-
|
|
207
|
+
o || (s = p.current) == null || s.click();
|
|
208
208
|
},
|
|
209
209
|
onKeyDown: (s) => {
|
|
210
210
|
var t;
|
|
211
|
-
!
|
|
211
|
+
!o && (s.key === "Enter" || s.key === " ") && (s.preventDefault(), (t = p.current) == null || t.click());
|
|
212
212
|
},
|
|
213
213
|
onDragOver: (s) => {
|
|
214
|
-
s.preventDefault(),
|
|
214
|
+
s.preventDefault(), o || M(!0);
|
|
215
215
|
},
|
|
216
216
|
onDragLeave: () => M(!1),
|
|
217
217
|
onDrop: ls,
|
|
@@ -226,7 +226,7 @@ function Is({
|
|
|
226
226
|
size: "sm",
|
|
227
227
|
variant: "secondary",
|
|
228
228
|
className: "mt-4",
|
|
229
|
-
disabled:
|
|
229
|
+
disabled: o,
|
|
230
230
|
onClick: (s) => {
|
|
231
231
|
var t;
|
|
232
232
|
s.stopPropagation(), (t = p.current) == null || t.click();
|
|
@@ -238,11 +238,11 @@ function Is({
|
|
|
238
238
|
}
|
|
239
239
|
),
|
|
240
240
|
j && /* @__PURE__ */ e.jsx("div", { className: "text-sm text-muted-foreground", children: j }),
|
|
241
|
-
/* @__PURE__ */ e.jsx("div", { className: R("space-y-3", as), children: l.length === 0 ? /* @__PURE__ */ e.jsx("div", { className: "rounded-md border border-dashed px-4 py-3 text-sm text-muted-foreground", children: U }) : l.map((s) => /* @__PURE__ */ e.jsx(
|
|
241
|
+
/* @__PURE__ */ e.jsx("div", { className: R("space-y-3", as), children: l.length === 0 ? /* @__PURE__ */ e.jsx("div", { className: "rounded-md border border-dashed bg-muted/20 px-4 py-3 text-sm text-muted-foreground", children: U }) : l.map((s) => /* @__PURE__ */ e.jsx(
|
|
242
242
|
"div",
|
|
243
243
|
{
|
|
244
|
-
className: "rounded-lg border bg-background px-4 py-3",
|
|
245
|
-
children: /* @__PURE__ */ e.jsxs("div", { className: "flex items-start gap-3", children: [
|
|
244
|
+
className: "rounded-lg border bg-background px-4 py-3 shadow-sm",
|
|
245
|
+
children: /* @__PURE__ */ e.jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [
|
|
246
246
|
/* @__PURE__ */ e.jsx("div", { className: "mt-0.5 rounded-md bg-muted p-2 text-muted-foreground", children: /* @__PURE__ */ e.jsx(hs, { className: "h-4 w-4" }) }),
|
|
247
247
|
/* @__PURE__ */ e.jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [
|
|
248
248
|
/* @__PURE__ */ e.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-2", children: [
|
|
@@ -273,7 +273,7 @@ function Is({
|
|
|
273
273
|
)
|
|
274
274
|
}
|
|
275
275
|
),
|
|
276
|
-
s.error && /* @__PURE__ */ e.jsx("div", { className: "text-xs text-destructive", children: s.error })
|
|
276
|
+
s.error && /* @__PURE__ */ e.jsx("div", { className: "break-words text-xs text-destructive", children: s.error })
|
|
277
277
|
] }),
|
|
278
278
|
/* @__PURE__ */ e.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
279
279
|
s.status === "error" && m && /* @__PURE__ */ e.jsx(
|
|
@@ -282,7 +282,7 @@ function Is({
|
|
|
282
282
|
type: "button",
|
|
283
283
|
variant: "ghost",
|
|
284
284
|
size: "sm",
|
|
285
|
-
disabled:
|
|
285
|
+
disabled: o || g,
|
|
286
286
|
onClick: () => xs(s),
|
|
287
287
|
children: _
|
|
288
288
|
}
|
|
@@ -294,7 +294,7 @@ function Is({
|
|
|
294
294
|
variant: "ghost",
|
|
295
295
|
size: "icon",
|
|
296
296
|
"aria-label": `移除 ${s.name}`,
|
|
297
|
-
disabled:
|
|
297
|
+
disabled: o || s.status === "uploading",
|
|
298
298
|
onClick: () => us(s.id),
|
|
299
299
|
children: /* @__PURE__ */ e.jsx(ps, { className: "h-4 w-4" })
|
|
300
300
|
}
|
|
@@ -311,7 +311,7 @@ function Is({
|
|
|
311
311
|
{
|
|
312
312
|
type: "button",
|
|
313
313
|
variant: "outline",
|
|
314
|
-
disabled:
|
|
314
|
+
disabled: o || g || l.length === 0,
|
|
315
315
|
onClick: () => D([]),
|
|
316
316
|
children: T
|
|
317
317
|
}
|
|
@@ -320,7 +320,7 @@ function Is({
|
|
|
320
320
|
f,
|
|
321
321
|
{
|
|
322
322
|
type: "button",
|
|
323
|
-
disabled:
|
|
323
|
+
disabled: o || g || !L || !m,
|
|
324
324
|
onClick: z,
|
|
325
325
|
children: S
|
|
326
326
|
}
|
|
@@ -331,7 +331,7 @@ function Is({
|
|
|
331
331
|
{
|
|
332
332
|
type: "button",
|
|
333
333
|
variant: "outline",
|
|
334
|
-
disabled:
|
|
334
|
+
disabled: o || g || l.length === 0,
|
|
335
335
|
onClick: () => D([]),
|
|
336
336
|
children: T
|
|
337
337
|
}
|
|
@@ -340,7 +340,7 @@ function Is({
|
|
|
340
340
|
f,
|
|
341
341
|
{
|
|
342
342
|
type: "button",
|
|
343
|
-
disabled:
|
|
343
|
+
disabled: o || g || !L || !m,
|
|
344
344
|
onClick: z,
|
|
345
345
|
children: S
|
|
346
346
|
}
|
package/dist/file-upload.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-upload.js","sources":["../src/components/FileUpload.tsx"],"sourcesContent":["import {\n CheckCircle2,\n FileText,\n Loader2,\n UploadCloud,\n X,\n XCircle,\n} from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\nimport type { DragEvent, HTMLAttributes, ReactNode } from 'react';\n\nimport { cn } from '../lib/utils';\nimport type {\n ButtonComponent,\n CardComponent,\n UIComponent,\n} from '../types/component-types';\n\nexport type FileUploadStatus = 'ready' | 'uploading' | 'success' | 'error';\n\nexport interface FileUploadItem<TResult = unknown> {\n id: string;\n file: File;\n name: string;\n size: number;\n type: string;\n status: FileUploadStatus;\n progress: number;\n error?: string;\n result?: TResult;\n}\n\nexport interface FileUploadHelpers {\n setProgress: (progress: number) => void;\n}\n\nexport interface FileUploadUIComponents {\n Card: CardComponent;\n CardHeader?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n CardTitle?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n CardDescription?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n CardContent: UIComponent<HTMLAttributes<HTMLDivElement>>;\n CardFooter?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n Button: ButtonComponent;\n}\n\nexport interface FileUploadProps<TResult = unknown> {\n components?: FileUploadUIComponents;\n title?: ReactNode;\n description?: ReactNode;\n helperText?: ReactNode;\n accept?: string;\n multiple?: boolean;\n maxFiles?: number;\n maxSize?: number;\n disabled?: boolean;\n autoUpload?: boolean;\n showUploadButton?: boolean;\n showResetButton?: boolean;\n selectLabel?: string;\n uploadLabel?: string;\n retryLabel?: string;\n resetLabel?: string;\n emptyLabel?: ReactNode;\n dropzoneLabel?: ReactNode;\n dropzoneDescription?: ReactNode;\n uploadingText?: string;\n successText?: string;\n errorText?: string;\n className?: string;\n dropzoneClassName?: string;\n listClassName?: string;\n formatFileSize?: (size: number) => string;\n validateFile?: (\n file: File,\n currentItems: FileUploadItem<TResult>[]\n ) => string | undefined;\n onFilesChange?: (items: FileUploadItem<TResult>[]) => void;\n onUpload?: (\n item: FileUploadItem<TResult>,\n helpers: FileUploadHelpers\n ) => Promise<TResult> | TResult;\n onUploadComplete?: (item: FileUploadItem<TResult>) => void;\n onUploadError?: (item: FileUploadItem<TResult>, error: unknown) => void;\n}\n\nconst defaultFormatFileSize = (size: number) => {\n if (size < 1024) return `${size} B`;\n if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;\n if (size < 1024 * 1024 * 1024) {\n return `${(size / 1024 / 1024).toFixed(1)} MB`;\n }\n return `${(size / 1024 / 1024 / 1024).toFixed(1)} GB`;\n};\n\nconst createFileId = (file: File) =>\n `${file.name}-${file.size}-${file.lastModified}-${Math.random()\n .toString(36)\n .slice(2)}`;\n\nconst getFileExtension = (fileName: string) => {\n const index = fileName.lastIndexOf('.');\n return index > -1 ? fileName.slice(index).toLowerCase() : '';\n};\n\nconst matchesAccept = (file: File, accept?: string) => {\n if (!accept) return true;\n\n const fileType = file.type.toLowerCase();\n const fileExtension = getFileExtension(file.name);\n\n return accept\n .split(',')\n .map((item) => item.trim().toLowerCase())\n .filter(Boolean)\n .some((rule) => {\n if (rule.startsWith('.')) {\n return fileExtension === rule;\n }\n if (rule.endsWith('/*')) {\n return fileType.startsWith(rule.slice(0, -1));\n }\n return fileType === rule;\n });\n};\n\nconst getErrorMessage = (error: unknown, fallback: string) => {\n if (error instanceof Error && error.message) {\n return error.message;\n }\n if (typeof error === 'string' && error) {\n return error;\n }\n return fallback;\n};\n\nexport function FileUpload<TResult = unknown>({\n components,\n title = '文件上传',\n description = '选择文件后开始上传,支持 Tale SDK 的直接上传和预签名上传流程。',\n helperText,\n accept,\n multiple = false,\n maxFiles,\n maxSize,\n disabled = false,\n autoUpload = false,\n showUploadButton,\n showResetButton = true,\n selectLabel = '选择文件',\n uploadLabel = '开始上传',\n retryLabel = '重试',\n resetLabel = '清空',\n emptyLabel = '尚未选择文件',\n dropzoneLabel = '拖拽文件到这里,或点击选择',\n dropzoneDescription = '上传前会先完成文件校验,上传逻辑由业务侧注入。',\n uploadingText = '上传中',\n successText = '上传完成',\n errorText = '上传失败',\n className,\n dropzoneClassName,\n listClassName,\n formatFileSize = defaultFormatFileSize,\n validateFile,\n onFilesChange,\n onUpload,\n onUploadComplete,\n onUploadError,\n}: FileUploadProps<TResult>) {\n const [items, setItems] = useState<FileUploadItem<TResult>[]>([]);\n const [isDragging, setIsDragging] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const shouldShowUploadButton = showUploadButton ?? Boolean(onUpload);\n const resolvedMaxFiles = multiple ? maxFiles : 1;\n const hasUploadableItems = items.some((item) => item.status === 'ready');\n const isUploading = items.some((item) => item.status === 'uploading');\n\n useEffect(() => {\n onFilesChange?.(items);\n }, [items, onFilesChange]);\n\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const {\n Card,\n CardHeader,\n CardTitle,\n CardDescription,\n CardContent,\n CardFooter,\n Button,\n } = components;\n\n const updateItems = (\n updater: (\n currentItems: FileUploadItem<TResult>[]\n ) => FileUploadItem<TResult>[]\n ) => {\n setItems((currentItems) => updater(currentItems));\n };\n\n const getValidationError = (\n file: File,\n currentItems: FileUploadItem<TResult>[]\n ) => {\n if (maxSize && file.size > maxSize) {\n return `文件不能超过 ${formatFileSize(maxSize)}`;\n }\n if (!matchesAccept(file, accept)) {\n return '文件类型不符合要求';\n }\n return validateFile?.(file, currentItems);\n };\n\n const createItemsFromFiles = (\n files: File[],\n currentItems: FileUploadItem<TResult>[]\n ) => {\n const nextItems = multiple ? [...currentItems] : [];\n const remainingSlots =\n resolvedMaxFiles === undefined\n ? files.length\n : Math.max(resolvedMaxFiles - nextItems.length, 0);\n\n files.slice(0, remainingSlots).forEach((file) => {\n const error = getValidationError(file, nextItems);\n nextItems.push({\n id: createFileId(file),\n file,\n name: file.name,\n size: file.size,\n type: file.type,\n status: error ? 'error' : 'ready',\n progress: 0,\n error,\n });\n });\n\n if (resolvedMaxFiles !== undefined && files.length > remainingSlots) {\n files.slice(remainingSlots).forEach((file) => {\n nextItems.push({\n id: createFileId(file),\n file,\n name: file.name,\n size: file.size,\n type: file.type,\n status: 'error',\n progress: 0,\n error: `最多只能选择 ${resolvedMaxFiles} 个文件`,\n });\n });\n }\n\n return nextItems;\n };\n\n const uploadItem = async (targetItem: FileUploadItem<TResult>) => {\n if (!onUpload || targetItem.status === 'uploading') return;\n\n updateItems((currentItems) =>\n currentItems.map((item) =>\n item.id === targetItem.id\n ? { ...item, status: 'uploading', progress: item.progress || 5 }\n : item\n )\n );\n\n const setProgress = (progress: number) => {\n updateItems((currentItems) =>\n currentItems.map((item) =>\n item.id === targetItem.id\n ? {\n ...item,\n progress: Math.max(0, Math.min(100, Math.round(progress))),\n }\n : item\n )\n );\n };\n\n try {\n const result = await onUpload(targetItem, { setProgress });\n const completedItem = {\n ...targetItem,\n status: 'success' as const,\n progress: 100,\n error: undefined,\n result,\n };\n updateItems((currentItems) =>\n currentItems.map((item) =>\n item.id === targetItem.id ? completedItem : item\n )\n );\n onUploadComplete?.(completedItem);\n } catch (error) {\n const failedItem = {\n ...targetItem,\n status: 'error' as const,\n progress: 0,\n error: getErrorMessage(error, errorText),\n };\n updateItems((currentItems) =>\n currentItems.map((item) =>\n item.id === targetItem.id ? failedItem : item\n )\n );\n onUploadError?.(failedItem, error);\n }\n };\n\n const uploadReadyItems = async () => {\n const readyItems = items.filter((item) => item.status === 'ready');\n for (const item of readyItems) {\n await uploadItem(item);\n }\n };\n\n const handleFiles = (fileList: FileList | null) => {\n if (!fileList || disabled) return;\n const files = Array.from(fileList);\n updateItems((currentItems) => {\n const nextItems = createItemsFromFiles(files, currentItems);\n if (autoUpload && onUpload) {\n nextItems\n .filter((item) => item.status === 'ready')\n .forEach((item) => {\n window.setTimeout(() => uploadItem(item), 0);\n });\n }\n return nextItems;\n });\n if (inputRef.current) {\n inputRef.current.value = '';\n }\n };\n\n const handleDrop = (event: DragEvent<HTMLDivElement>) => {\n event.preventDefault();\n setIsDragging(false);\n handleFiles(event.dataTransfer.files);\n };\n\n const handleRemove = (id: string) => {\n updateItems((currentItems) =>\n currentItems.filter((item) => item.id !== id)\n );\n };\n\n const handleRetry = (item: FileUploadItem<TResult>) => {\n const error = getValidationError(\n item.file,\n items.filter((currentItem) => currentItem.id !== item.id)\n );\n if (error) {\n updateItems((currentItems) =>\n currentItems.map((currentItem) =>\n currentItem.id === item.id ? { ...currentItem, error } : currentItem\n )\n );\n return;\n }\n uploadItem({ ...item, status: 'ready', error: undefined, progress: 0 });\n };\n\n const renderStatus = (item: FileUploadItem<TResult>) => {\n if (item.status === 'uploading') {\n return (\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-blue-600 dark:text-blue-300\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n {uploadingText}\n </span>\n );\n }\n if (item.status === 'success') {\n return (\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-emerald-600 dark:text-emerald-300\">\n <CheckCircle2 className=\"h-3.5 w-3.5\" />\n {successText}\n </span>\n );\n }\n if (item.status === 'error') {\n return (\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-destructive\">\n <XCircle className=\"h-3.5 w-3.5\" />\n {errorText}\n </span>\n );\n }\n return <span className=\"text-xs text-muted-foreground\">待上传</span>;\n };\n\n const header = (\n <>\n {title && CardTitle && <CardTitle>{title}</CardTitle>}\n {title && !CardTitle && (\n <div className=\"text-lg font-semibold leading-none tracking-tight\">\n {title}\n </div>\n )}\n {description && CardDescription && (\n <CardDescription>{description}</CardDescription>\n )}\n {description && !CardDescription && (\n <div className=\"text-sm text-muted-foreground\">{description}</div>\n )}\n </>\n );\n\n return (\n <Card className={cn('overflow-hidden', className)}>\n {CardHeader && (title || description) ? (\n <CardHeader>{header}</CardHeader>\n ) : (\n (title || description) && (\n <div className=\"space-y-1.5 p-6\">{header}</div>\n )\n )}\n <CardContent className=\"space-y-4\">\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n className=\"sr-only\"\n onChange={(event) => handleFiles(event.target.files)}\n disabled={disabled}\n />\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled}\n className={cn(\n 'flex min-h-40 cursor-pointer flex-col items-center justify-center rounded-lg border border-dashed border-muted-foreground/30 bg-muted/20 px-6 py-8 text-center transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',\n isDragging && 'border-primary bg-primary/5',\n disabled && 'cursor-not-allowed opacity-60',\n dropzoneClassName\n )}\n onClick={() => {\n if (!disabled) {\n inputRef.current?.click();\n }\n }}\n onKeyDown={(event) => {\n if (!disabled && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n inputRef.current?.click();\n }\n }}\n onDragOver={(event) => {\n event.preventDefault();\n if (!disabled) {\n setIsDragging(true);\n }\n }}\n onDragLeave={() => setIsDragging(false)}\n onDrop={handleDrop}\n >\n <div className=\"mb-3 rounded-full bg-primary/10 p-3 text-primary\">\n <UploadCloud className=\"h-6 w-6\" />\n </div>\n <div className=\"text-sm font-medium\">{dropzoneLabel}</div>\n {dropzoneDescription && (\n <div className=\"mt-1 max-w-md text-sm text-muted-foreground\">\n {dropzoneDescription}\n </div>\n )}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"mt-4\"\n disabled={disabled}\n onClick={(event) => {\n event.stopPropagation();\n inputRef.current?.click();\n }}\n >\n {selectLabel}\n </Button>\n </div>\n\n {helperText && (\n <div className=\"text-sm text-muted-foreground\">{helperText}</div>\n )}\n\n <div className={cn('space-y-3', listClassName)}>\n {items.length === 0 ? (\n <div className=\"rounded-md border border-dashed px-4 py-3 text-sm text-muted-foreground\">\n {emptyLabel}\n </div>\n ) : (\n items.map((item) => (\n <div\n key={item.id}\n className=\"rounded-lg border bg-background px-4 py-3\"\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"mt-0.5 rounded-md bg-muted p-2 text-muted-foreground\">\n <FileText className=\"h-4 w-4\" />\n </div>\n <div className=\"min-w-0 flex-1 space-y-2\">\n <div className=\"flex flex-wrap items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium\">\n {item.name}\n </div>\n <div className=\"text-xs text-muted-foreground\">\n {formatFileSize(item.size)}\n {item.type ? ` · ${item.type}` : ''}\n </div>\n </div>\n {renderStatus(item)}\n </div>\n {item.status === 'uploading' && (\n <div\n className=\"h-2 overflow-hidden rounded-full bg-muted\"\n aria-label={`${item.name} 上传进度`}\n role=\"progressbar\"\n aria-valuenow={item.progress}\n aria-valuemin={0}\n aria-valuemax={100}\n >\n <div\n className=\"h-full rounded-full bg-primary transition-all\"\n style={{ width: `${item.progress}%` }}\n />\n </div>\n )}\n {item.error && (\n <div className=\"text-xs text-destructive\">\n {item.error}\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {item.status === 'error' && onUpload && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={disabled || isUploading}\n onClick={() => handleRetry(item)}\n >\n {retryLabel}\n </Button>\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`移除 ${item.name}`}\n disabled={disabled || item.status === 'uploading'}\n onClick={() => handleRemove(item.id)}\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n ))\n )}\n </div>\n </CardContent>\n {(shouldShowUploadButton || showResetButton) &&\n (CardFooter ? (\n <CardFooter className=\"justify-end gap-2\">\n {showResetButton && (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={disabled || isUploading || items.length === 0}\n onClick={() => setItems([])}\n >\n {resetLabel}\n </Button>\n )}\n {shouldShowUploadButton && (\n <Button\n type=\"button\"\n disabled={\n disabled || isUploading || !hasUploadableItems || !onUpload\n }\n onClick={uploadReadyItems}\n >\n {uploadLabel}\n </Button>\n )}\n </CardFooter>\n ) : (\n <div className=\"flex justify-end gap-2 p-6 pt-0\">\n {showResetButton && (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={disabled || isUploading || items.length === 0}\n onClick={() => setItems([])}\n >\n {resetLabel}\n </Button>\n )}\n {shouldShowUploadButton && (\n <Button\n type=\"button\"\n disabled={\n disabled || isUploading || !hasUploadableItems || !onUpload\n }\n onClick={uploadReadyItems}\n >\n {uploadLabel}\n </Button>\n )}\n </div>\n ))}\n </Card>\n );\n}\n"],"names":["defaultFormatFileSize","size","createFileId","file","getFileExtension","fileName","index","matchesAccept","accept","fileType","fileExtension","item","rule","getErrorMessage","error","fallback","FileUpload","components","title","description","helperText","multiple","maxFiles","maxSize","disabled","autoUpload","showUploadButton","showResetButton","selectLabel","uploadLabel","retryLabel","resetLabel","emptyLabel","dropzoneLabel","dropzoneDescription","uploadingText","successText","errorText","className","dropzoneClassName","listClassName","formatFileSize","validateFile","onFilesChange","onUpload","onUploadComplete","onUploadError","items","setItems","useState","isDragging","setIsDragging","inputRef","useRef","shouldShowUploadButton","resolvedMaxFiles","hasUploadableItems","isUploading","useEffect","jsx","Card","CardHeader","CardTitle","CardDescription","CardContent","CardFooter","Button","updateItems","updater","currentItems","getValidationError","createItemsFromFiles","files","nextItems","remainingSlots","uploadItem","targetItem","setProgress","progress","result","completedItem","failedItem","uploadReadyItems","readyItems","handleFiles","fileList","handleDrop","event","handleRemove","id","handleRetry","currentItem","renderStatus","jsxs","Loader2","CheckCircle2","XCircle","header","Fragment","cn","_a","UploadCloud","FileText","X"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"file-upload.js","sources":["../src/components/FileUpload.tsx"],"sourcesContent":["import {\n CheckCircle2,\n FileText,\n Loader2,\n UploadCloud,\n X,\n XCircle,\n} from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\nimport type { DragEvent, HTMLAttributes, ReactNode } from 'react';\n\nimport { cn } from '../lib/utils';\nimport type {\n ButtonComponent,\n CardComponent,\n UIComponent,\n} from '../types/component-types';\n\nexport type FileUploadStatus = 'ready' | 'uploading' | 'success' | 'error';\n\nexport interface FileUploadItem<TResult = unknown> {\n id: string;\n file: File;\n name: string;\n size: number;\n type: string;\n status: FileUploadStatus;\n progress: number;\n error?: string;\n result?: TResult;\n}\n\nexport interface FileUploadHelpers {\n setProgress: (progress: number) => void;\n}\n\nexport interface FileUploadUIComponents {\n Card: CardComponent;\n CardHeader?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n CardTitle?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n CardDescription?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n CardContent: UIComponent<HTMLAttributes<HTMLDivElement>>;\n CardFooter?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n Button: ButtonComponent;\n}\n\nexport interface FileUploadProps<TResult = unknown> {\n components?: FileUploadUIComponents;\n title?: ReactNode;\n description?: ReactNode;\n helperText?: ReactNode;\n accept?: string;\n multiple?: boolean;\n maxFiles?: number;\n maxSize?: number;\n disabled?: boolean;\n autoUpload?: boolean;\n showUploadButton?: boolean;\n showResetButton?: boolean;\n selectLabel?: string;\n uploadLabel?: string;\n retryLabel?: string;\n resetLabel?: string;\n emptyLabel?: ReactNode;\n dropzoneLabel?: ReactNode;\n dropzoneDescription?: ReactNode;\n uploadingText?: string;\n successText?: string;\n errorText?: string;\n className?: string;\n dropzoneClassName?: string;\n listClassName?: string;\n formatFileSize?: (size: number) => string;\n validateFile?: (\n file: File,\n currentItems: FileUploadItem<TResult>[]\n ) => string | undefined;\n onFilesChange?: (items: FileUploadItem<TResult>[]) => void;\n onUpload?: (\n item: FileUploadItem<TResult>,\n helpers: FileUploadHelpers\n ) => Promise<TResult> | TResult;\n onUploadComplete?: (item: FileUploadItem<TResult>) => void;\n onUploadError?: (item: FileUploadItem<TResult>, error: unknown) => void;\n}\n\nconst defaultFormatFileSize = (size: number) => {\n if (size < 1024) return `${size} B`;\n if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;\n if (size < 1024 * 1024 * 1024) {\n return `${(size / 1024 / 1024).toFixed(1)} MB`;\n }\n return `${(size / 1024 / 1024 / 1024).toFixed(1)} GB`;\n};\n\nconst createFileId = (file: File) =>\n `${file.name}-${file.size}-${file.lastModified}-${Math.random()\n .toString(36)\n .slice(2)}`;\n\nconst getFileExtension = (fileName: string) => {\n const index = fileName.lastIndexOf('.');\n return index > -1 ? fileName.slice(index).toLowerCase() : '';\n};\n\nconst matchesAccept = (file: File, accept?: string) => {\n if (!accept) return true;\n\n const fileType = file.type.toLowerCase();\n const fileExtension = getFileExtension(file.name);\n\n return accept\n .split(',')\n .map((item) => item.trim().toLowerCase())\n .filter(Boolean)\n .some((rule) => {\n if (rule.startsWith('.')) {\n return fileExtension === rule;\n }\n if (rule.endsWith('/*')) {\n return fileType.startsWith(rule.slice(0, -1));\n }\n return fileType === rule;\n });\n};\n\nconst getErrorMessage = (error: unknown, fallback: string) => {\n if (error instanceof Error && error.message) {\n return error.message;\n }\n if (typeof error === 'string' && error) {\n return error;\n }\n return fallback;\n};\n\nexport function FileUpload<TResult = unknown>({\n components,\n title = '文件上传',\n description = '选择文件后开始上传,支持 Tale SDK 的直接上传和预签名上传流程。',\n helperText,\n accept,\n multiple = false,\n maxFiles,\n maxSize,\n disabled = false,\n autoUpload = false,\n showUploadButton,\n showResetButton = true,\n selectLabel = '选择文件',\n uploadLabel = '开始上传',\n retryLabel = '重试',\n resetLabel = '清空',\n emptyLabel = '尚未选择文件',\n dropzoneLabel = '拖拽文件到这里,或点击选择',\n dropzoneDescription = '上传前会先完成文件校验,上传逻辑由业务侧注入。',\n uploadingText = '上传中',\n successText = '上传完成',\n errorText = '上传失败',\n className,\n dropzoneClassName,\n listClassName,\n formatFileSize = defaultFormatFileSize,\n validateFile,\n onFilesChange,\n onUpload,\n onUploadComplete,\n onUploadError,\n}: FileUploadProps<TResult>) {\n const [items, setItems] = useState<FileUploadItem<TResult>[]>([]);\n const [isDragging, setIsDragging] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const shouldShowUploadButton = showUploadButton ?? Boolean(onUpload);\n const resolvedMaxFiles = multiple ? maxFiles : 1;\n const hasUploadableItems = items.some((item) => item.status === 'ready');\n const isUploading = items.some((item) => item.status === 'uploading');\n\n useEffect(() => {\n onFilesChange?.(items);\n }, [items, onFilesChange]);\n\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const {\n Card,\n CardHeader,\n CardTitle,\n CardDescription,\n CardContent,\n CardFooter,\n Button,\n } = components;\n\n const updateItems = (\n updater: (\n currentItems: FileUploadItem<TResult>[]\n ) => FileUploadItem<TResult>[]\n ) => {\n setItems((currentItems) => updater(currentItems));\n };\n\n const getValidationError = (\n file: File,\n currentItems: FileUploadItem<TResult>[]\n ) => {\n if (maxSize && file.size > maxSize) {\n return `文件不能超过 ${formatFileSize(maxSize)}`;\n }\n if (!matchesAccept(file, accept)) {\n return '文件类型不符合要求';\n }\n return validateFile?.(file, currentItems);\n };\n\n const createItemsFromFiles = (\n files: File[],\n currentItems: FileUploadItem<TResult>[]\n ) => {\n const nextItems = multiple ? [...currentItems] : [];\n const remainingSlots =\n resolvedMaxFiles === undefined\n ? files.length\n : Math.max(resolvedMaxFiles - nextItems.length, 0);\n\n files.slice(0, remainingSlots).forEach((file) => {\n const error = getValidationError(file, nextItems);\n nextItems.push({\n id: createFileId(file),\n file,\n name: file.name,\n size: file.size,\n type: file.type,\n status: error ? 'error' : 'ready',\n progress: 0,\n error,\n });\n });\n\n if (resolvedMaxFiles !== undefined && files.length > remainingSlots) {\n files.slice(remainingSlots).forEach((file) => {\n nextItems.push({\n id: createFileId(file),\n file,\n name: file.name,\n size: file.size,\n type: file.type,\n status: 'error',\n progress: 0,\n error: `最多只能选择 ${resolvedMaxFiles} 个文件`,\n });\n });\n }\n\n return nextItems;\n };\n\n const uploadItem = async (targetItem: FileUploadItem<TResult>) => {\n if (!onUpload || targetItem.status === 'uploading') return;\n\n updateItems((currentItems) =>\n currentItems.map((item) =>\n item.id === targetItem.id\n ? { ...item, status: 'uploading', progress: item.progress || 5 }\n : item\n )\n );\n\n const setProgress = (progress: number) => {\n updateItems((currentItems) =>\n currentItems.map((item) =>\n item.id === targetItem.id\n ? {\n ...item,\n progress: Math.max(0, Math.min(100, Math.round(progress))),\n }\n : item\n )\n );\n };\n\n try {\n const result = await onUpload(targetItem, { setProgress });\n const completedItem = {\n ...targetItem,\n status: 'success' as const,\n progress: 100,\n error: undefined,\n result,\n };\n updateItems((currentItems) =>\n currentItems.map((item) =>\n item.id === targetItem.id ? completedItem : item\n )\n );\n onUploadComplete?.(completedItem);\n } catch (error) {\n const failedItem = {\n ...targetItem,\n status: 'error' as const,\n progress: 0,\n error: getErrorMessage(error, errorText),\n };\n updateItems((currentItems) =>\n currentItems.map((item) =>\n item.id === targetItem.id ? failedItem : item\n )\n );\n onUploadError?.(failedItem, error);\n }\n };\n\n const uploadReadyItems = async () => {\n const readyItems = items.filter((item) => item.status === 'ready');\n for (const item of readyItems) {\n await uploadItem(item);\n }\n };\n\n const handleFiles = (fileList: FileList | null) => {\n if (!fileList || disabled) return;\n const files = Array.from(fileList);\n updateItems((currentItems) => {\n const nextItems = createItemsFromFiles(files, currentItems);\n if (autoUpload && onUpload) {\n nextItems\n .filter((item) => item.status === 'ready')\n .forEach((item) => {\n window.setTimeout(() => uploadItem(item), 0);\n });\n }\n return nextItems;\n });\n if (inputRef.current) {\n inputRef.current.value = '';\n }\n };\n\n const handleDrop = (event: DragEvent<HTMLDivElement>) => {\n event.preventDefault();\n setIsDragging(false);\n handleFiles(event.dataTransfer.files);\n };\n\n const handleRemove = (id: string) => {\n updateItems((currentItems) =>\n currentItems.filter((item) => item.id !== id)\n );\n };\n\n const handleRetry = (item: FileUploadItem<TResult>) => {\n const error = getValidationError(\n item.file,\n items.filter((currentItem) => currentItem.id !== item.id)\n );\n if (error) {\n updateItems((currentItems) =>\n currentItems.map((currentItem) =>\n currentItem.id === item.id ? { ...currentItem, error } : currentItem\n )\n );\n return;\n }\n uploadItem({ ...item, status: 'ready', error: undefined, progress: 0 });\n };\n\n const renderStatus = (item: FileUploadItem<TResult>) => {\n if (item.status === 'uploading') {\n return (\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-blue-600 dark:text-blue-300\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n {uploadingText}\n </span>\n );\n }\n if (item.status === 'success') {\n return (\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-emerald-600 dark:text-emerald-300\">\n <CheckCircle2 className=\"h-3.5 w-3.5\" />\n {successText}\n </span>\n );\n }\n if (item.status === 'error') {\n return (\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-destructive\">\n <XCircle className=\"h-3.5 w-3.5\" />\n {errorText}\n </span>\n );\n }\n return <span className=\"text-xs text-muted-foreground\">待上传</span>;\n };\n\n const header = (\n <>\n {title && CardTitle && <CardTitle>{title}</CardTitle>}\n {title && !CardTitle && (\n <div className=\"text-lg font-semibold leading-none tracking-tight\">\n {title}\n </div>\n )}\n {description && CardDescription && (\n <CardDescription>{description}</CardDescription>\n )}\n {description && !CardDescription && (\n <div className=\"text-sm text-muted-foreground\">{description}</div>\n )}\n </>\n );\n\n return (\n <Card className={cn('overflow-hidden', className)}>\n {CardHeader && (title || description) ? (\n <CardHeader>{header}</CardHeader>\n ) : (\n (title || description) && (\n <div className=\"space-y-1.5 p-6\">{header}</div>\n )\n )}\n <CardContent className=\"space-y-4\">\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n className=\"sr-only\"\n onChange={(event) => handleFiles(event.target.files)}\n disabled={disabled}\n />\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled}\n className={cn(\n 'flex min-h-40 cursor-pointer touch-manipulation flex-col items-center justify-center rounded-lg border border-dashed border-muted-foreground/30 bg-muted/20 px-6 py-8 text-center transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n isDragging && 'border-primary bg-primary/10 shadow-inner',\n disabled && 'cursor-not-allowed opacity-60',\n dropzoneClassName\n )}\n onClick={() => {\n if (!disabled) {\n inputRef.current?.click();\n }\n }}\n onKeyDown={(event) => {\n if (!disabled && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n inputRef.current?.click();\n }\n }}\n onDragOver={(event) => {\n event.preventDefault();\n if (!disabled) {\n setIsDragging(true);\n }\n }}\n onDragLeave={() => setIsDragging(false)}\n onDrop={handleDrop}\n >\n <div className=\"mb-3 rounded-full bg-primary/10 p-3 text-primary\">\n <UploadCloud className=\"h-6 w-6\" />\n </div>\n <div className=\"text-sm font-medium\">{dropzoneLabel}</div>\n {dropzoneDescription && (\n <div className=\"mt-1 max-w-md text-sm text-muted-foreground\">\n {dropzoneDescription}\n </div>\n )}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"mt-4\"\n disabled={disabled}\n onClick={(event) => {\n event.stopPropagation();\n inputRef.current?.click();\n }}\n >\n {selectLabel}\n </Button>\n </div>\n\n {helperText && (\n <div className=\"text-sm text-muted-foreground\">{helperText}</div>\n )}\n\n <div className={cn('space-y-3', listClassName)}>\n {items.length === 0 ? (\n <div className=\"rounded-md border border-dashed bg-muted/20 px-4 py-3 text-sm text-muted-foreground\">\n {emptyLabel}\n </div>\n ) : (\n items.map((item) => (\n <div\n key={item.id}\n className=\"rounded-lg border bg-background px-4 py-3 shadow-sm\"\n >\n <div className=\"flex min-w-0 items-start gap-3\">\n <div className=\"mt-0.5 rounded-md bg-muted p-2 text-muted-foreground\">\n <FileText className=\"h-4 w-4\" />\n </div>\n <div className=\"min-w-0 flex-1 space-y-2\">\n <div className=\"flex flex-wrap items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium\">\n {item.name}\n </div>\n <div className=\"text-xs text-muted-foreground\">\n {formatFileSize(item.size)}\n {item.type ? ` · ${item.type}` : ''}\n </div>\n </div>\n {renderStatus(item)}\n </div>\n {item.status === 'uploading' && (\n <div\n className=\"h-2 overflow-hidden rounded-full bg-muted\"\n aria-label={`${item.name} 上传进度`}\n role=\"progressbar\"\n aria-valuenow={item.progress}\n aria-valuemin={0}\n aria-valuemax={100}\n >\n <div\n className=\"h-full rounded-full bg-primary transition-all\"\n style={{ width: `${item.progress}%` }}\n />\n </div>\n )}\n {item.error && (\n <div className=\"break-words text-xs text-destructive\">\n {item.error}\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {item.status === 'error' && onUpload && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={disabled || isUploading}\n onClick={() => handleRetry(item)}\n >\n {retryLabel}\n </Button>\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`移除 ${item.name}`}\n disabled={disabled || item.status === 'uploading'}\n onClick={() => handleRemove(item.id)}\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n ))\n )}\n </div>\n </CardContent>\n {(shouldShowUploadButton || showResetButton) &&\n (CardFooter ? (\n <CardFooter className=\"justify-end gap-2\">\n {showResetButton && (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={disabled || isUploading || items.length === 0}\n onClick={() => setItems([])}\n >\n {resetLabel}\n </Button>\n )}\n {shouldShowUploadButton && (\n <Button\n type=\"button\"\n disabled={\n disabled || isUploading || !hasUploadableItems || !onUpload\n }\n onClick={uploadReadyItems}\n >\n {uploadLabel}\n </Button>\n )}\n </CardFooter>\n ) : (\n <div className=\"flex justify-end gap-2 p-6 pt-0\">\n {showResetButton && (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={disabled || isUploading || items.length === 0}\n onClick={() => setItems([])}\n >\n {resetLabel}\n </Button>\n )}\n {shouldShowUploadButton && (\n <Button\n type=\"button\"\n disabled={\n disabled || isUploading || !hasUploadableItems || !onUpload\n }\n onClick={uploadReadyItems}\n >\n {uploadLabel}\n </Button>\n )}\n </div>\n ))}\n </Card>\n );\n}\n"],"names":["defaultFormatFileSize","size","createFileId","file","getFileExtension","fileName","index","matchesAccept","accept","fileType","fileExtension","item","rule","getErrorMessage","error","fallback","FileUpload","components","title","description","helperText","multiple","maxFiles","maxSize","disabled","autoUpload","showUploadButton","showResetButton","selectLabel","uploadLabel","retryLabel","resetLabel","emptyLabel","dropzoneLabel","dropzoneDescription","uploadingText","successText","errorText","className","dropzoneClassName","listClassName","formatFileSize","validateFile","onFilesChange","onUpload","onUploadComplete","onUploadError","items","setItems","useState","isDragging","setIsDragging","inputRef","useRef","shouldShowUploadButton","resolvedMaxFiles","hasUploadableItems","isUploading","useEffect","jsx","Card","CardHeader","CardTitle","CardDescription","CardContent","CardFooter","Button","updateItems","updater","currentItems","getValidationError","createItemsFromFiles","files","nextItems","remainingSlots","uploadItem","targetItem","setProgress","progress","result","completedItem","failedItem","uploadReadyItems","readyItems","handleFiles","fileList","handleDrop","event","handleRemove","id","handleRetry","currentItem","renderStatus","jsxs","Loader2","CheckCircle2","XCircle","header","Fragment","cn","_a","UploadCloud","FileText","X"],"mappings":"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"}
|
package/dist/image-reader.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./jsx-runtime-BB_1_6y_.cjs"),u=require("lucide-react"),r=require("react"),I=require("./utils-IjLH3w2e.cjs"),$=require("./media-utils-X1dDYP9W.cjs"),z=["jpg","jpeg","png","gif","webp","svg","bmp","avif","ico"],L=["image/jpeg","image/png","image/gif","image/webp","image/svg+xml","image/bmp","image/avif","image/x-icon","image/vnd.microsoft.icon"],pe=s=>s?s instanceof Error?s:new Error(s):null,je=(s,c,a)=>Math.min(Math.max(s,c),a);function Ee({src:s,alt:c="",fileName:a,mimeType:d,components:B,loading:F=!1,error:X,className:M,containerClassName:Y,imageClassName:Z,toolbarClassName:H,loadingText:J="正在加载图片...",errorText:S="图片加载失败",unsupportedText:K="暂不支持该图片格式",showToolbar:Q=!0,showOpenInNewTab:V=!0,objectFit:W="contain",initialScale:C=1,minScale:p=.25,maxScale:j=4,scaleStep:
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./jsx-runtime-BB_1_6y_.cjs"),u=require("lucide-react"),r=require("react"),I=require("./utils-IjLH3w2e.cjs"),$=require("./media-utils-X1dDYP9W.cjs"),z=["jpg","jpeg","png","gif","webp","svg","bmp","avif","ico"],L=["image/jpeg","image/png","image/gif","image/webp","image/svg+xml","image/bmp","image/avif","image/x-icon","image/vnd.microsoft.icon"],pe=s=>s?s instanceof Error?s:new Error(s):null,je=(s,c,a)=>Math.min(Math.max(s,c),a);function Ee({src:s,alt:c="",fileName:a,mimeType:d,components:B,loading:F=!1,error:X,className:M,containerClassName:Y,imageClassName:Z,toolbarClassName:H,loadingText:J="正在加载图片...",errorText:S="图片加载失败",unsupportedText:K="暂不支持该图片格式",showToolbar:Q=!0,showOpenInNewTab:V=!0,objectFit:W="contain",initialScale:C=1,minScale:p=.25,maxScale:j=4,scaleStep:y=.25,scale:E,onScaleChange:l,rotation:f,onRotationChange:m,allowUnsupportedFormat:ee=!1,supportedExtensions:te=z,supportedMimeTypes:se=L,onLoad:g,onError:R,style:ne,...re}){const{Card:P,CardContent:_,Button:O,Skeleton:b}=B||{},[ie,oe]=r.useState(C),[ae,xe]=r.useState(0),[ue,h]=r.useState(!0),[le,v]=r.useState(null),o=E??ie,w=f??ae,i=pe(X)||le,k=ee||$.isSupportedMediaSource({src:s,fileName:a,mimeType:d,supportedExtensions:te,supportedMimeTypes:se}),q=F||ue&&!i,T=r.useCallback(t=>{const n=je(t,p,j);E===void 0&&oe(n),l==null||l(n)},[E,j,p,l]),U=r.useCallback(t=>{const n=(t%360+360)%360;f===void 0&&xe(n),m==null||m(n)},[f,m]),me=r.useMemo(()=>{const t=$.getMediaExtension(s,a);return t?t.toUpperCase():d||"图片"},[a,d,s]);r.useEffect(()=>{h(!0),v(null)},[s]);const x=(t,n,D,G)=>{const de="inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50";return O?e.jsxRuntimeExports.jsx(O,{"aria-label":t,className:"h-8 w-8",disabled:G,onClick:D,size:"icon",title:t,type:"button",variant:"outline",children:n}):e.jsxRuntimeExports.jsx("button",{"aria-label":t,className:de,disabled:G,onClick:D,title:t,type:"button",children:n})},ce=()=>e.jsxRuntimeExports.jsx("div",{className:"w-full space-y-3 p-4",role:"status","aria-live":"polite",children:b?e.jsxRuntimeExports.jsxs(e.jsxRuntimeExports.Fragment,{children:[e.jsxRuntimeExports.jsx(b,{className:"h-4 w-32"}),e.jsxRuntimeExports.jsx(b,{className:"h-80 w-full"})]}):e.jsxRuntimeExports.jsx("p",{className:"text-sm text-muted-foreground",children:J})}),A=t=>e.jsxRuntimeExports.jsxs("div",{className:"p-4 text-center text-sm text-destructive",role:"alert",children:[e.jsxRuntimeExports.jsx("p",{className:"font-medium",children:t||S}),i!=null&&i.message?e.jsxRuntimeExports.jsx("p",{className:"mt-1 opacity-80",children:i.message}):null]}),N=e.jsxRuntimeExports.jsxs("div",{className:I.cn("flex h-full min-h-[360px] flex-col overflow-hidden rounded-md border bg-background",Y),children:[Q?e.jsxRuntimeExports.jsxs("div",{className:I.cn("flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2",H),children:[e.jsxRuntimeExports.jsx("span",{className:"text-xs font-medium text-muted-foreground",children:me}),e.jsxRuntimeExports.jsxs("div",{className:"flex items-center gap-1",children:[x("缩小",e.jsxRuntimeExports.jsx(u.ZoomOut,{className:"h-4 w-4"}),()=>T(o-y),o<=p),e.jsxRuntimeExports.jsxs("span",{className:"min-w-14 text-center text-xs text-muted-foreground",children:[Math.round(o*100),"%"]}),x("放大",e.jsxRuntimeExports.jsx(u.ZoomIn,{className:"h-4 w-4"}),()=>T(o+y),o>=j),x("向左旋转",e.jsxRuntimeExports.jsx(u.RotateCcw,{className:"h-4 w-4"}),()=>U(w-90)),x("向右旋转",e.jsxRuntimeExports.jsx(u.RotateCw,{className:"h-4 w-4"}),()=>U(w+90)),V?x("新窗口打开",e.jsxRuntimeExports.jsx(u.ExternalLink,{className:"h-4 w-4"}),()=>window.open(s,"_blank","noreferrer")):null]})]}):null,e.jsxRuntimeExports.jsxs("div",{className:"relative flex min-h-0 flex-1 items-center justify-center overflow-auto bg-muted/20 p-4",children:[k?i?A():null:A(K),k&&!i?e.jsxRuntimeExports.jsxs(e.jsxRuntimeExports.Fragment,{children:[q?ce():null,e.jsxRuntimeExports.jsx("img",{...re,alt:c,className:I.cn("max-h-full max-w-full transition-transform",q?"invisible absolute":"visible",Z),onError:()=>{const t=new Error(S);h(!1),v(t),R==null||R(t)},onLoad:()=>{h(!1),v(null),g==null||g()},src:s,decoding:"async",style:{objectFit:W,transform:`scale(${o}) rotate(${w}deg)`,transformOrigin:"center center",...ne}})]}):null]})]});return P?e.jsxRuntimeExports.jsx(P,{className:M,children:_?e.jsxRuntimeExports.jsx(_,{children:N}):N}):e.jsxRuntimeExports.jsx("div",{className:M,children:N})}exports.ImageReader=Ee;exports.SUPPORTED_IMAGE_EXTENSIONS=z;exports.SUPPORTED_IMAGE_MIME_TYPES=L;
|
|
2
2
|
//# sourceMappingURL=image-reader.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-reader.cjs","sources":["../src/components/ImageReader.tsx"],"sourcesContent":["import {\n ExternalLink as ExternalLinkIcon,\n RotateCcw as RotateCcwIcon,\n RotateCw as RotateCwIcon,\n ZoomIn as ZoomInIcon,\n ZoomOut as ZoomOutIcon,\n} from 'lucide-react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport type { HTMLAttributes, ImgHTMLAttributes, ReactNode } from 'react';\n\nimport { cn } from '../lib/utils';\nimport type {\n ButtonComponent,\n CardComponent,\n SkeletonComponent,\n UIComponent,\n} from '../types/component-types';\n\nimport { getMediaExtension, isSupportedMediaSource } from './media-utils';\n\nexport const SUPPORTED_IMAGE_EXTENSIONS = [\n 'jpg',\n 'jpeg',\n 'png',\n 'gif',\n 'webp',\n 'svg',\n 'bmp',\n 'avif',\n 'ico',\n] as const;\n\nexport const SUPPORTED_IMAGE_MIME_TYPES = [\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/svg+xml',\n 'image/bmp',\n 'image/avif',\n 'image/x-icon',\n 'image/vnd.microsoft.icon',\n] as const;\n\nexport interface ImageReaderUIComponents {\n Card?: CardComponent;\n CardContent?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n Button?: ButtonComponent;\n Skeleton?: SkeletonComponent;\n}\n\nexport interface ImageReaderProps\n extends Omit<\n ImgHTMLAttributes<HTMLImageElement>,\n | 'alt'\n | 'children'\n | 'className'\n | 'loading'\n | 'onError'\n | 'onLoad'\n | 'src'\n > {\n src: string;\n alt?: string;\n fileName?: string;\n mimeType?: string;\n components?: ImageReaderUIComponents;\n loading?: boolean;\n error?: Error | string | null;\n className?: string;\n containerClassName?: string;\n imageClassName?: string;\n toolbarClassName?: string;\n loadingText?: string;\n errorText?: string;\n unsupportedText?: string;\n showToolbar?: boolean;\n showOpenInNewTab?: boolean;\n objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';\n initialScale?: number;\n minScale?: number;\n maxScale?: number;\n scaleStep?: number;\n scale?: number;\n onScaleChange?: (scale: number) => void;\n rotation?: number;\n onRotationChange?: (rotation: number) => void;\n allowUnsupportedFormat?: boolean;\n supportedExtensions?: readonly string[];\n supportedMimeTypes?: readonly string[];\n onLoad?: () => void;\n onError?: (error: Error) => void;\n}\n\nconst normalizeError = (error: Error | string | null | undefined) => {\n if (!error) return null;\n return error instanceof Error ? error : new Error(error);\n};\n\nconst clamp = (value: number, min: number, max: number) =>\n Math.min(Math.max(value, min), max);\n\nexport function ImageReader({\n src,\n alt = '',\n fileName,\n mimeType,\n components,\n loading = false,\n error,\n className,\n containerClassName,\n imageClassName,\n toolbarClassName,\n loadingText = '正在加载图片...',\n errorText = '图片加载失败',\n unsupportedText = '暂不支持该图片格式',\n showToolbar = true,\n showOpenInNewTab = true,\n objectFit = 'contain',\n initialScale = 1,\n minScale = 0.25,\n maxScale = 4,\n scaleStep = 0.25,\n scale: controlledScale,\n onScaleChange,\n rotation: controlledRotation,\n onRotationChange,\n allowUnsupportedFormat = false,\n supportedExtensions = SUPPORTED_IMAGE_EXTENSIONS,\n supportedMimeTypes = SUPPORTED_IMAGE_MIME_TYPES,\n onLoad,\n onError,\n style,\n ...imageProps\n}: ImageReaderProps) {\n const { Card, CardContent, Button, Skeleton } = components || {};\n const [internalScale, setInternalScale] = useState(initialScale);\n const [internalRotation, setInternalRotation] = useState(0);\n const [isImageLoading, setIsImageLoading] = useState(true);\n const [imageError, setImageError] = useState<Error | null>(null);\n\n const scale = controlledScale ?? internalScale;\n const rotation = controlledRotation ?? internalRotation;\n const displayedError = normalizeError(error) || imageError;\n const isSupported =\n allowUnsupportedFormat ||\n isSupportedMediaSource({\n src,\n fileName,\n mimeType,\n supportedExtensions,\n supportedMimeTypes,\n });\n const isLoading = loading || (isImageLoading && !displayedError);\n\n const setNextScale = useCallback(\n (nextScale: number) => {\n const clampedScale = clamp(nextScale, minScale, maxScale);\n if (controlledScale === undefined) {\n setInternalScale(clampedScale);\n }\n onScaleChange?.(clampedScale);\n },\n [controlledScale, maxScale, minScale, onScaleChange]\n );\n\n const setNextRotation = useCallback(\n (nextRotation: number) => {\n const normalizedRotation = ((nextRotation % 360) + 360) % 360;\n if (controlledRotation === undefined) {\n setInternalRotation(normalizedRotation);\n }\n onRotationChange?.(normalizedRotation);\n },\n [controlledRotation, onRotationChange]\n );\n\n const formatLabel = useMemo(() => {\n const extension = getMediaExtension(src, fileName);\n return extension ? extension.toUpperCase() : mimeType || '图片';\n }, [fileName, mimeType, src]);\n\n useEffect(() => {\n setIsImageLoading(true);\n setImageError(null);\n }, [src]);\n\n const renderButton = (\n label: string,\n icon: ReactNode,\n onClick: () => void,\n disabled?: boolean\n ) => {\n const buttonClassName =\n 'inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50';\n\n if (Button) {\n return (\n <Button\n aria-label={label}\n className=\"h-8 w-8\"\n disabled={disabled}\n onClick={onClick}\n size=\"icon\"\n title={label}\n type=\"button\"\n variant=\"outline\"\n >\n {icon}\n </Button>\n );\n }\n\n return (\n <button\n aria-label={label}\n className={buttonClassName}\n disabled={disabled}\n onClick={onClick}\n title={label}\n type=\"button\"\n >\n {icon}\n </button>\n );\n };\n\n const renderLoading = () => (\n <div className=\"w-full space-y-3 p-4\" role=\"status\" aria-live=\"polite\">\n {Skeleton ? (\n <>\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-80 w-full\" />\n </>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n )}\n </div>\n );\n\n const renderError = (message?: string) => (\n <div className=\"p-4 text-center text-sm text-destructive\" role=\"alert\">\n <p className=\"font-medium\">{message || errorText}</p>\n {displayedError?.message ? (\n <p className=\"mt-1 opacity-80\">{displayedError.message}</p>\n ) : null}\n </div>\n );\n\n const body = (\n <div\n className={cn(\n 'flex h-full min-h-[360px] flex-col overflow-hidden rounded-md border bg-background',\n containerClassName\n )}\n >\n {showToolbar ? (\n <div\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2',\n toolbarClassName\n )}\n >\n <span className=\"text-xs font-medium text-muted-foreground\">\n {formatLabel}\n </span>\n <div className=\"flex items-center gap-1\">\n {renderButton(\n '缩小',\n <ZoomOutIcon className=\"h-4 w-4\" />,\n () => setNextScale(scale - scaleStep),\n scale <= minScale\n )}\n <span className=\"min-w-14 text-center text-xs text-muted-foreground\">\n {Math.round(scale * 100)}%\n </span>\n {renderButton(\n '放大',\n <ZoomInIcon className=\"h-4 w-4\" />,\n () => setNextScale(scale + scaleStep),\n scale >= maxScale\n )}\n {renderButton(\n '向左旋转',\n <RotateCcwIcon className=\"h-4 w-4\" />,\n () => setNextRotation(rotation - 90)\n )}\n {renderButton(\n '向右旋转',\n <RotateCwIcon className=\"h-4 w-4\" />,\n () => setNextRotation(rotation + 90)\n )}\n {showOpenInNewTab\n ? renderButton(\n '新窗口打开',\n <ExternalLinkIcon className=\"h-4 w-4\" />,\n () => window.open(src, '_blank', 'noreferrer')\n )\n : null}\n </div>\n </div>\n ) : null}\n <div className=\"relative flex min-h-0 flex-1 items-center justify-center overflow-auto bg-muted/20 p-4\">\n {!isSupported\n ? renderError(unsupportedText)\n : displayedError\n ? renderError()\n : null}\n {isSupported && !displayedError ? (\n <>\n {isLoading ? renderLoading() : null}\n <img\n {...imageProps}\n alt={alt}\n className={cn(\n 'max-h-full max-w-full transition-transform',\n isLoading ? 'invisible absolute' : 'visible',\n imageClassName\n )}\n onError={() => {\n const nextError = new Error(errorText);\n setIsImageLoading(false);\n setImageError(nextError);\n onError?.(nextError);\n }}\n onLoad={() => {\n setIsImageLoading(false);\n setImageError(null);\n onLoad?.();\n }}\n src={src}\n style={{\n objectFit,\n transform: `scale(${scale}) rotate(${rotation}deg)`,\n transformOrigin: 'center center',\n ...style,\n }}\n />\n </>\n ) : null}\n </div>\n </div>\n );\n\n if (Card) {\n return (\n <Card className={className}>\n {CardContent ? <CardContent>{body}</CardContent> : body}\n </Card>\n );\n }\n\n return <div className={className}>{body}</div>;\n}\n"],"names":["SUPPORTED_IMAGE_EXTENSIONS","SUPPORTED_IMAGE_MIME_TYPES","normalizeError","error","clamp","value","min","max","ImageReader","src","alt","fileName","mimeType","components","loading","className","containerClassName","imageClassName","toolbarClassName","loadingText","errorText","unsupportedText","showToolbar","showOpenInNewTab","objectFit","initialScale","minScale","maxScale","scaleStep","controlledScale","onScaleChange","controlledRotation","onRotationChange","allowUnsupportedFormat","supportedExtensions","supportedMimeTypes","onLoad","onError","style","imageProps","Card","CardContent","Button","Skeleton","internalScale","setInternalScale","useState","internalRotation","setInternalRotation","isImageLoading","setIsImageLoading","imageError","setImageError","scale","rotation","displayedError","isSupported","isSupportedMediaSource","isLoading","setNextScale","useCallback","nextScale","clampedScale","setNextRotation","nextRotation","normalizedRotation","formatLabel","useMemo","extension","getMediaExtension","useEffect","renderButton","label","icon","onClick","disabled","buttonClassName","jsx","renderLoading","jsxs","Fragment","renderError","message","body","cn","ZoomOutIcon","ZoomInIcon","RotateCcwIcon","RotateCwIcon","ExternalLinkIcon","nextError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"image-reader.cjs","sources":["../src/components/ImageReader.tsx"],"sourcesContent":["import {\n ExternalLink as ExternalLinkIcon,\n RotateCcw as RotateCcwIcon,\n RotateCw as RotateCwIcon,\n ZoomIn as ZoomInIcon,\n ZoomOut as ZoomOutIcon,\n} from 'lucide-react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport type { HTMLAttributes, ImgHTMLAttributes, ReactNode } from 'react';\n\nimport { cn } from '../lib/utils';\nimport type {\n ButtonComponent,\n CardComponent,\n SkeletonComponent,\n UIComponent,\n} from '../types/component-types';\n\nimport { getMediaExtension, isSupportedMediaSource } from './media-utils';\n\nexport const SUPPORTED_IMAGE_EXTENSIONS = [\n 'jpg',\n 'jpeg',\n 'png',\n 'gif',\n 'webp',\n 'svg',\n 'bmp',\n 'avif',\n 'ico',\n] as const;\n\nexport const SUPPORTED_IMAGE_MIME_TYPES = [\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/svg+xml',\n 'image/bmp',\n 'image/avif',\n 'image/x-icon',\n 'image/vnd.microsoft.icon',\n] as const;\n\nexport interface ImageReaderUIComponents {\n Card?: CardComponent;\n CardContent?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n Button?: ButtonComponent;\n Skeleton?: SkeletonComponent;\n}\n\nexport interface ImageReaderProps extends Omit<\n ImgHTMLAttributes<HTMLImageElement>,\n 'alt' | 'children' | 'className' | 'loading' | 'onError' | 'onLoad' | 'src'\n> {\n src: string;\n alt?: string;\n fileName?: string;\n mimeType?: string;\n components?: ImageReaderUIComponents;\n loading?: boolean;\n error?: Error | string | null;\n className?: string;\n containerClassName?: string;\n imageClassName?: string;\n toolbarClassName?: string;\n loadingText?: string;\n errorText?: string;\n unsupportedText?: string;\n showToolbar?: boolean;\n showOpenInNewTab?: boolean;\n objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';\n initialScale?: number;\n minScale?: number;\n maxScale?: number;\n scaleStep?: number;\n scale?: number;\n onScaleChange?: (scale: number) => void;\n rotation?: number;\n onRotationChange?: (rotation: number) => void;\n allowUnsupportedFormat?: boolean;\n supportedExtensions?: readonly string[];\n supportedMimeTypes?: readonly string[];\n onLoad?: () => void;\n onError?: (error: Error) => void;\n}\n\nconst normalizeError = (error: Error | string | null | undefined) => {\n if (!error) return null;\n return error instanceof Error ? error : new Error(error);\n};\n\nconst clamp = (value: number, min: number, max: number) =>\n Math.min(Math.max(value, min), max);\n\nexport function ImageReader({\n src,\n alt = '',\n fileName,\n mimeType,\n components,\n loading = false,\n error,\n className,\n containerClassName,\n imageClassName,\n toolbarClassName,\n loadingText = '正在加载图片...',\n errorText = '图片加载失败',\n unsupportedText = '暂不支持该图片格式',\n showToolbar = true,\n showOpenInNewTab = true,\n objectFit = 'contain',\n initialScale = 1,\n minScale = 0.25,\n maxScale = 4,\n scaleStep = 0.25,\n scale: controlledScale,\n onScaleChange,\n rotation: controlledRotation,\n onRotationChange,\n allowUnsupportedFormat = false,\n supportedExtensions = SUPPORTED_IMAGE_EXTENSIONS,\n supportedMimeTypes = SUPPORTED_IMAGE_MIME_TYPES,\n onLoad,\n onError,\n style,\n ...imageProps\n}: ImageReaderProps) {\n const { Card, CardContent, Button, Skeleton } = components || {};\n const [internalScale, setInternalScale] = useState(initialScale);\n const [internalRotation, setInternalRotation] = useState(0);\n const [isImageLoading, setIsImageLoading] = useState(true);\n const [imageError, setImageError] = useState<Error | null>(null);\n\n const scale = controlledScale ?? internalScale;\n const rotation = controlledRotation ?? internalRotation;\n const displayedError = normalizeError(error) || imageError;\n const isSupported =\n allowUnsupportedFormat ||\n isSupportedMediaSource({\n src,\n fileName,\n mimeType,\n supportedExtensions,\n supportedMimeTypes,\n });\n const isLoading = loading || (isImageLoading && !displayedError);\n\n const setNextScale = useCallback(\n (nextScale: number) => {\n const clampedScale = clamp(nextScale, minScale, maxScale);\n if (controlledScale === undefined) {\n setInternalScale(clampedScale);\n }\n onScaleChange?.(clampedScale);\n },\n [controlledScale, maxScale, minScale, onScaleChange]\n );\n\n const setNextRotation = useCallback(\n (nextRotation: number) => {\n const normalizedRotation = ((nextRotation % 360) + 360) % 360;\n if (controlledRotation === undefined) {\n setInternalRotation(normalizedRotation);\n }\n onRotationChange?.(normalizedRotation);\n },\n [controlledRotation, onRotationChange]\n );\n\n const formatLabel = useMemo(() => {\n const extension = getMediaExtension(src, fileName);\n return extension ? extension.toUpperCase() : mimeType || '图片';\n }, [fileName, mimeType, src]);\n\n useEffect(() => {\n setIsImageLoading(true);\n setImageError(null);\n }, [src]);\n\n const renderButton = (\n label: string,\n icon: ReactNode,\n onClick: () => void,\n disabled?: boolean\n ) => {\n const buttonClassName =\n 'inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50';\n\n if (Button) {\n return (\n <Button\n aria-label={label}\n className=\"h-8 w-8\"\n disabled={disabled}\n onClick={onClick}\n size=\"icon\"\n title={label}\n type=\"button\"\n variant=\"outline\"\n >\n {icon}\n </Button>\n );\n }\n\n return (\n <button\n aria-label={label}\n className={buttonClassName}\n disabled={disabled}\n onClick={onClick}\n title={label}\n type=\"button\"\n >\n {icon}\n </button>\n );\n };\n\n const renderLoading = () => (\n <div className=\"w-full space-y-3 p-4\" role=\"status\" aria-live=\"polite\">\n {Skeleton ? (\n <>\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-80 w-full\" />\n </>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n )}\n </div>\n );\n\n const renderError = (message?: string) => (\n <div className=\"p-4 text-center text-sm text-destructive\" role=\"alert\">\n <p className=\"font-medium\">{message || errorText}</p>\n {displayedError?.message ? (\n <p className=\"mt-1 opacity-80\">{displayedError.message}</p>\n ) : null}\n </div>\n );\n\n const body = (\n <div\n className={cn(\n 'flex h-full min-h-[360px] flex-col overflow-hidden rounded-md border bg-background',\n containerClassName\n )}\n >\n {showToolbar ? (\n <div\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2',\n toolbarClassName\n )}\n >\n <span className=\"text-xs font-medium text-muted-foreground\">\n {formatLabel}\n </span>\n <div className=\"flex items-center gap-1\">\n {renderButton(\n '缩小',\n <ZoomOutIcon className=\"h-4 w-4\" />,\n () => setNextScale(scale - scaleStep),\n scale <= minScale\n )}\n <span className=\"min-w-14 text-center text-xs text-muted-foreground\">\n {Math.round(scale * 100)}%\n </span>\n {renderButton(\n '放大',\n <ZoomInIcon className=\"h-4 w-4\" />,\n () => setNextScale(scale + scaleStep),\n scale >= maxScale\n )}\n {renderButton(\n '向左旋转',\n <RotateCcwIcon className=\"h-4 w-4\" />,\n () => setNextRotation(rotation - 90)\n )}\n {renderButton(\n '向右旋转',\n <RotateCwIcon className=\"h-4 w-4\" />,\n () => setNextRotation(rotation + 90)\n )}\n {showOpenInNewTab\n ? renderButton(\n '新窗口打开',\n <ExternalLinkIcon className=\"h-4 w-4\" />,\n () => window.open(src, '_blank', 'noreferrer')\n )\n : null}\n </div>\n </div>\n ) : null}\n <div className=\"relative flex min-h-0 flex-1 items-center justify-center overflow-auto bg-muted/20 p-4\">\n {!isSupported\n ? renderError(unsupportedText)\n : displayedError\n ? renderError()\n : null}\n {isSupported && !displayedError ? (\n <>\n {isLoading ? renderLoading() : null}\n <img\n {...imageProps}\n alt={alt}\n className={cn(\n 'max-h-full max-w-full transition-transform',\n isLoading ? 'invisible absolute' : 'visible',\n imageClassName\n )}\n onError={() => {\n const nextError = new Error(errorText);\n setIsImageLoading(false);\n setImageError(nextError);\n onError?.(nextError);\n }}\n onLoad={() => {\n setIsImageLoading(false);\n setImageError(null);\n onLoad?.();\n }}\n src={src}\n decoding=\"async\"\n style={{\n objectFit,\n transform: `scale(${scale}) rotate(${rotation}deg)`,\n transformOrigin: 'center center',\n ...style,\n }}\n />\n </>\n ) : null}\n </div>\n </div>\n );\n\n if (Card) {\n return (\n <Card className={className}>\n {CardContent ? <CardContent>{body}</CardContent> : body}\n </Card>\n );\n }\n\n return <div className={className}>{body}</div>;\n}\n"],"names":["SUPPORTED_IMAGE_EXTENSIONS","SUPPORTED_IMAGE_MIME_TYPES","normalizeError","error","clamp","value","min","max","ImageReader","src","alt","fileName","mimeType","components","loading","className","containerClassName","imageClassName","toolbarClassName","loadingText","errorText","unsupportedText","showToolbar","showOpenInNewTab","objectFit","initialScale","minScale","maxScale","scaleStep","controlledScale","onScaleChange","controlledRotation","onRotationChange","allowUnsupportedFormat","supportedExtensions","supportedMimeTypes","onLoad","onError","style","imageProps","Card","CardContent","Button","Skeleton","internalScale","setInternalScale","useState","internalRotation","setInternalRotation","isImageLoading","setIsImageLoading","imageError","setImageError","scale","rotation","displayedError","isSupported","isSupportedMediaSource","isLoading","setNextScale","useCallback","nextScale","clampedScale","setNextRotation","nextRotation","normalizedRotation","formatLabel","useMemo","extension","getMediaExtension","useEffect","renderButton","label","icon","onClick","disabled","buttonClassName","jsx","renderLoading","jsxs","Fragment","renderError","message","body","cn","ZoomOutIcon","ZoomInIcon","RotateCcwIcon","RotateCwIcon","ExternalLinkIcon","nextError"],"mappings":"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"}
|
package/dist/image-reader.js
CHANGED
package/dist/image-reader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-reader.js","sources":["../src/components/ImageReader.tsx"],"sourcesContent":["import {\n ExternalLink as ExternalLinkIcon,\n RotateCcw as RotateCcwIcon,\n RotateCw as RotateCwIcon,\n ZoomIn as ZoomInIcon,\n ZoomOut as ZoomOutIcon,\n} from 'lucide-react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport type { HTMLAttributes, ImgHTMLAttributes, ReactNode } from 'react';\n\nimport { cn } from '../lib/utils';\nimport type {\n ButtonComponent,\n CardComponent,\n SkeletonComponent,\n UIComponent,\n} from '../types/component-types';\n\nimport { getMediaExtension, isSupportedMediaSource } from './media-utils';\n\nexport const SUPPORTED_IMAGE_EXTENSIONS = [\n 'jpg',\n 'jpeg',\n 'png',\n 'gif',\n 'webp',\n 'svg',\n 'bmp',\n 'avif',\n 'ico',\n] as const;\n\nexport const SUPPORTED_IMAGE_MIME_TYPES = [\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/svg+xml',\n 'image/bmp',\n 'image/avif',\n 'image/x-icon',\n 'image/vnd.microsoft.icon',\n] as const;\n\nexport interface ImageReaderUIComponents {\n Card?: CardComponent;\n CardContent?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n Button?: ButtonComponent;\n Skeleton?: SkeletonComponent;\n}\n\nexport interface ImageReaderProps\n extends Omit<\n ImgHTMLAttributes<HTMLImageElement>,\n | 'alt'\n | 'children'\n | 'className'\n | 'loading'\n | 'onError'\n | 'onLoad'\n | 'src'\n > {\n src: string;\n alt?: string;\n fileName?: string;\n mimeType?: string;\n components?: ImageReaderUIComponents;\n loading?: boolean;\n error?: Error | string | null;\n className?: string;\n containerClassName?: string;\n imageClassName?: string;\n toolbarClassName?: string;\n loadingText?: string;\n errorText?: string;\n unsupportedText?: string;\n showToolbar?: boolean;\n showOpenInNewTab?: boolean;\n objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';\n initialScale?: number;\n minScale?: number;\n maxScale?: number;\n scaleStep?: number;\n scale?: number;\n onScaleChange?: (scale: number) => void;\n rotation?: number;\n onRotationChange?: (rotation: number) => void;\n allowUnsupportedFormat?: boolean;\n supportedExtensions?: readonly string[];\n supportedMimeTypes?: readonly string[];\n onLoad?: () => void;\n onError?: (error: Error) => void;\n}\n\nconst normalizeError = (error: Error | string | null | undefined) => {\n if (!error) return null;\n return error instanceof Error ? error : new Error(error);\n};\n\nconst clamp = (value: number, min: number, max: number) =>\n Math.min(Math.max(value, min), max);\n\nexport function ImageReader({\n src,\n alt = '',\n fileName,\n mimeType,\n components,\n loading = false,\n error,\n className,\n containerClassName,\n imageClassName,\n toolbarClassName,\n loadingText = '正在加载图片...',\n errorText = '图片加载失败',\n unsupportedText = '暂不支持该图片格式',\n showToolbar = true,\n showOpenInNewTab = true,\n objectFit = 'contain',\n initialScale = 1,\n minScale = 0.25,\n maxScale = 4,\n scaleStep = 0.25,\n scale: controlledScale,\n onScaleChange,\n rotation: controlledRotation,\n onRotationChange,\n allowUnsupportedFormat = false,\n supportedExtensions = SUPPORTED_IMAGE_EXTENSIONS,\n supportedMimeTypes = SUPPORTED_IMAGE_MIME_TYPES,\n onLoad,\n onError,\n style,\n ...imageProps\n}: ImageReaderProps) {\n const { Card, CardContent, Button, Skeleton } = components || {};\n const [internalScale, setInternalScale] = useState(initialScale);\n const [internalRotation, setInternalRotation] = useState(0);\n const [isImageLoading, setIsImageLoading] = useState(true);\n const [imageError, setImageError] = useState<Error | null>(null);\n\n const scale = controlledScale ?? internalScale;\n const rotation = controlledRotation ?? internalRotation;\n const displayedError = normalizeError(error) || imageError;\n const isSupported =\n allowUnsupportedFormat ||\n isSupportedMediaSource({\n src,\n fileName,\n mimeType,\n supportedExtensions,\n supportedMimeTypes,\n });\n const isLoading = loading || (isImageLoading && !displayedError);\n\n const setNextScale = useCallback(\n (nextScale: number) => {\n const clampedScale = clamp(nextScale, minScale, maxScale);\n if (controlledScale === undefined) {\n setInternalScale(clampedScale);\n }\n onScaleChange?.(clampedScale);\n },\n [controlledScale, maxScale, minScale, onScaleChange]\n );\n\n const setNextRotation = useCallback(\n (nextRotation: number) => {\n const normalizedRotation = ((nextRotation % 360) + 360) % 360;\n if (controlledRotation === undefined) {\n setInternalRotation(normalizedRotation);\n }\n onRotationChange?.(normalizedRotation);\n },\n [controlledRotation, onRotationChange]\n );\n\n const formatLabel = useMemo(() => {\n const extension = getMediaExtension(src, fileName);\n return extension ? extension.toUpperCase() : mimeType || '图片';\n }, [fileName, mimeType, src]);\n\n useEffect(() => {\n setIsImageLoading(true);\n setImageError(null);\n }, [src]);\n\n const renderButton = (\n label: string,\n icon: ReactNode,\n onClick: () => void,\n disabled?: boolean\n ) => {\n const buttonClassName =\n 'inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50';\n\n if (Button) {\n return (\n <Button\n aria-label={label}\n className=\"h-8 w-8\"\n disabled={disabled}\n onClick={onClick}\n size=\"icon\"\n title={label}\n type=\"button\"\n variant=\"outline\"\n >\n {icon}\n </Button>\n );\n }\n\n return (\n <button\n aria-label={label}\n className={buttonClassName}\n disabled={disabled}\n onClick={onClick}\n title={label}\n type=\"button\"\n >\n {icon}\n </button>\n );\n };\n\n const renderLoading = () => (\n <div className=\"w-full space-y-3 p-4\" role=\"status\" aria-live=\"polite\">\n {Skeleton ? (\n <>\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-80 w-full\" />\n </>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n )}\n </div>\n );\n\n const renderError = (message?: string) => (\n <div className=\"p-4 text-center text-sm text-destructive\" role=\"alert\">\n <p className=\"font-medium\">{message || errorText}</p>\n {displayedError?.message ? (\n <p className=\"mt-1 opacity-80\">{displayedError.message}</p>\n ) : null}\n </div>\n );\n\n const body = (\n <div\n className={cn(\n 'flex h-full min-h-[360px] flex-col overflow-hidden rounded-md border bg-background',\n containerClassName\n )}\n >\n {showToolbar ? (\n <div\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2',\n toolbarClassName\n )}\n >\n <span className=\"text-xs font-medium text-muted-foreground\">\n {formatLabel}\n </span>\n <div className=\"flex items-center gap-1\">\n {renderButton(\n '缩小',\n <ZoomOutIcon className=\"h-4 w-4\" />,\n () => setNextScale(scale - scaleStep),\n scale <= minScale\n )}\n <span className=\"min-w-14 text-center text-xs text-muted-foreground\">\n {Math.round(scale * 100)}%\n </span>\n {renderButton(\n '放大',\n <ZoomInIcon className=\"h-4 w-4\" />,\n () => setNextScale(scale + scaleStep),\n scale >= maxScale\n )}\n {renderButton(\n '向左旋转',\n <RotateCcwIcon className=\"h-4 w-4\" />,\n () => setNextRotation(rotation - 90)\n )}\n {renderButton(\n '向右旋转',\n <RotateCwIcon className=\"h-4 w-4\" />,\n () => setNextRotation(rotation + 90)\n )}\n {showOpenInNewTab\n ? renderButton(\n '新窗口打开',\n <ExternalLinkIcon className=\"h-4 w-4\" />,\n () => window.open(src, '_blank', 'noreferrer')\n )\n : null}\n </div>\n </div>\n ) : null}\n <div className=\"relative flex min-h-0 flex-1 items-center justify-center overflow-auto bg-muted/20 p-4\">\n {!isSupported\n ? renderError(unsupportedText)\n : displayedError\n ? renderError()\n : null}\n {isSupported && !displayedError ? (\n <>\n {isLoading ? renderLoading() : null}\n <img\n {...imageProps}\n alt={alt}\n className={cn(\n 'max-h-full max-w-full transition-transform',\n isLoading ? 'invisible absolute' : 'visible',\n imageClassName\n )}\n onError={() => {\n const nextError = new Error(errorText);\n setIsImageLoading(false);\n setImageError(nextError);\n onError?.(nextError);\n }}\n onLoad={() => {\n setIsImageLoading(false);\n setImageError(null);\n onLoad?.();\n }}\n src={src}\n style={{\n objectFit,\n transform: `scale(${scale}) rotate(${rotation}deg)`,\n transformOrigin: 'center center',\n ...style,\n }}\n />\n </>\n ) : null}\n </div>\n </div>\n );\n\n if (Card) {\n return (\n <Card className={className}>\n {CardContent ? <CardContent>{body}</CardContent> : body}\n </Card>\n );\n }\n\n return <div className={className}>{body}</div>;\n}\n"],"names":["SUPPORTED_IMAGE_EXTENSIONS","SUPPORTED_IMAGE_MIME_TYPES","normalizeError","error","clamp","value","min","max","ImageReader","src","alt","fileName","mimeType","components","loading","className","containerClassName","imageClassName","toolbarClassName","loadingText","errorText","unsupportedText","showToolbar","showOpenInNewTab","objectFit","initialScale","minScale","maxScale","scaleStep","controlledScale","onScaleChange","controlledRotation","onRotationChange","allowUnsupportedFormat","supportedExtensions","supportedMimeTypes","onLoad","onError","style","imageProps","Card","CardContent","Button","Skeleton","internalScale","setInternalScale","useState","internalRotation","setInternalRotation","isImageLoading","setIsImageLoading","imageError","setImageError","scale","rotation","displayedError","isSupported","isSupportedMediaSource","isLoading","setNextScale","useCallback","nextScale","clampedScale","setNextRotation","nextRotation","normalizedRotation","formatLabel","useMemo","extension","getMediaExtension","useEffect","renderButton","label","icon","onClick","disabled","buttonClassName","jsx","renderLoading","jsxs","Fragment","renderError","message","body","cn","ZoomOutIcon","ZoomInIcon","RotateCcwIcon","RotateCwIcon","ExternalLinkIcon","nextError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"image-reader.js","sources":["../src/components/ImageReader.tsx"],"sourcesContent":["import {\n ExternalLink as ExternalLinkIcon,\n RotateCcw as RotateCcwIcon,\n RotateCw as RotateCwIcon,\n ZoomIn as ZoomInIcon,\n ZoomOut as ZoomOutIcon,\n} from 'lucide-react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport type { HTMLAttributes, ImgHTMLAttributes, ReactNode } from 'react';\n\nimport { cn } from '../lib/utils';\nimport type {\n ButtonComponent,\n CardComponent,\n SkeletonComponent,\n UIComponent,\n} from '../types/component-types';\n\nimport { getMediaExtension, isSupportedMediaSource } from './media-utils';\n\nexport const SUPPORTED_IMAGE_EXTENSIONS = [\n 'jpg',\n 'jpeg',\n 'png',\n 'gif',\n 'webp',\n 'svg',\n 'bmp',\n 'avif',\n 'ico',\n] as const;\n\nexport const SUPPORTED_IMAGE_MIME_TYPES = [\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/svg+xml',\n 'image/bmp',\n 'image/avif',\n 'image/x-icon',\n 'image/vnd.microsoft.icon',\n] as const;\n\nexport interface ImageReaderUIComponents {\n Card?: CardComponent;\n CardContent?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n Button?: ButtonComponent;\n Skeleton?: SkeletonComponent;\n}\n\nexport interface ImageReaderProps extends Omit<\n ImgHTMLAttributes<HTMLImageElement>,\n 'alt' | 'children' | 'className' | 'loading' | 'onError' | 'onLoad' | 'src'\n> {\n src: string;\n alt?: string;\n fileName?: string;\n mimeType?: string;\n components?: ImageReaderUIComponents;\n loading?: boolean;\n error?: Error | string | null;\n className?: string;\n containerClassName?: string;\n imageClassName?: string;\n toolbarClassName?: string;\n loadingText?: string;\n errorText?: string;\n unsupportedText?: string;\n showToolbar?: boolean;\n showOpenInNewTab?: boolean;\n objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';\n initialScale?: number;\n minScale?: number;\n maxScale?: number;\n scaleStep?: number;\n scale?: number;\n onScaleChange?: (scale: number) => void;\n rotation?: number;\n onRotationChange?: (rotation: number) => void;\n allowUnsupportedFormat?: boolean;\n supportedExtensions?: readonly string[];\n supportedMimeTypes?: readonly string[];\n onLoad?: () => void;\n onError?: (error: Error) => void;\n}\n\nconst normalizeError = (error: Error | string | null | undefined) => {\n if (!error) return null;\n return error instanceof Error ? error : new Error(error);\n};\n\nconst clamp = (value: number, min: number, max: number) =>\n Math.min(Math.max(value, min), max);\n\nexport function ImageReader({\n src,\n alt = '',\n fileName,\n mimeType,\n components,\n loading = false,\n error,\n className,\n containerClassName,\n imageClassName,\n toolbarClassName,\n loadingText = '正在加载图片...',\n errorText = '图片加载失败',\n unsupportedText = '暂不支持该图片格式',\n showToolbar = true,\n showOpenInNewTab = true,\n objectFit = 'contain',\n initialScale = 1,\n minScale = 0.25,\n maxScale = 4,\n scaleStep = 0.25,\n scale: controlledScale,\n onScaleChange,\n rotation: controlledRotation,\n onRotationChange,\n allowUnsupportedFormat = false,\n supportedExtensions = SUPPORTED_IMAGE_EXTENSIONS,\n supportedMimeTypes = SUPPORTED_IMAGE_MIME_TYPES,\n onLoad,\n onError,\n style,\n ...imageProps\n}: ImageReaderProps) {\n const { Card, CardContent, Button, Skeleton } = components || {};\n const [internalScale, setInternalScale] = useState(initialScale);\n const [internalRotation, setInternalRotation] = useState(0);\n const [isImageLoading, setIsImageLoading] = useState(true);\n const [imageError, setImageError] = useState<Error | null>(null);\n\n const scale = controlledScale ?? internalScale;\n const rotation = controlledRotation ?? internalRotation;\n const displayedError = normalizeError(error) || imageError;\n const isSupported =\n allowUnsupportedFormat ||\n isSupportedMediaSource({\n src,\n fileName,\n mimeType,\n supportedExtensions,\n supportedMimeTypes,\n });\n const isLoading = loading || (isImageLoading && !displayedError);\n\n const setNextScale = useCallback(\n (nextScale: number) => {\n const clampedScale = clamp(nextScale, minScale, maxScale);\n if (controlledScale === undefined) {\n setInternalScale(clampedScale);\n }\n onScaleChange?.(clampedScale);\n },\n [controlledScale, maxScale, minScale, onScaleChange]\n );\n\n const setNextRotation = useCallback(\n (nextRotation: number) => {\n const normalizedRotation = ((nextRotation % 360) + 360) % 360;\n if (controlledRotation === undefined) {\n setInternalRotation(normalizedRotation);\n }\n onRotationChange?.(normalizedRotation);\n },\n [controlledRotation, onRotationChange]\n );\n\n const formatLabel = useMemo(() => {\n const extension = getMediaExtension(src, fileName);\n return extension ? extension.toUpperCase() : mimeType || '图片';\n }, [fileName, mimeType, src]);\n\n useEffect(() => {\n setIsImageLoading(true);\n setImageError(null);\n }, [src]);\n\n const renderButton = (\n label: string,\n icon: ReactNode,\n onClick: () => void,\n disabled?: boolean\n ) => {\n const buttonClassName =\n 'inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50';\n\n if (Button) {\n return (\n <Button\n aria-label={label}\n className=\"h-8 w-8\"\n disabled={disabled}\n onClick={onClick}\n size=\"icon\"\n title={label}\n type=\"button\"\n variant=\"outline\"\n >\n {icon}\n </Button>\n );\n }\n\n return (\n <button\n aria-label={label}\n className={buttonClassName}\n disabled={disabled}\n onClick={onClick}\n title={label}\n type=\"button\"\n >\n {icon}\n </button>\n );\n };\n\n const renderLoading = () => (\n <div className=\"w-full space-y-3 p-4\" role=\"status\" aria-live=\"polite\">\n {Skeleton ? (\n <>\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-80 w-full\" />\n </>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n )}\n </div>\n );\n\n const renderError = (message?: string) => (\n <div className=\"p-4 text-center text-sm text-destructive\" role=\"alert\">\n <p className=\"font-medium\">{message || errorText}</p>\n {displayedError?.message ? (\n <p className=\"mt-1 opacity-80\">{displayedError.message}</p>\n ) : null}\n </div>\n );\n\n const body = (\n <div\n className={cn(\n 'flex h-full min-h-[360px] flex-col overflow-hidden rounded-md border bg-background',\n containerClassName\n )}\n >\n {showToolbar ? (\n <div\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2',\n toolbarClassName\n )}\n >\n <span className=\"text-xs font-medium text-muted-foreground\">\n {formatLabel}\n </span>\n <div className=\"flex items-center gap-1\">\n {renderButton(\n '缩小',\n <ZoomOutIcon className=\"h-4 w-4\" />,\n () => setNextScale(scale - scaleStep),\n scale <= minScale\n )}\n <span className=\"min-w-14 text-center text-xs text-muted-foreground\">\n {Math.round(scale * 100)}%\n </span>\n {renderButton(\n '放大',\n <ZoomInIcon className=\"h-4 w-4\" />,\n () => setNextScale(scale + scaleStep),\n scale >= maxScale\n )}\n {renderButton(\n '向左旋转',\n <RotateCcwIcon className=\"h-4 w-4\" />,\n () => setNextRotation(rotation - 90)\n )}\n {renderButton(\n '向右旋转',\n <RotateCwIcon className=\"h-4 w-4\" />,\n () => setNextRotation(rotation + 90)\n )}\n {showOpenInNewTab\n ? renderButton(\n '新窗口打开',\n <ExternalLinkIcon className=\"h-4 w-4\" />,\n () => window.open(src, '_blank', 'noreferrer')\n )\n : null}\n </div>\n </div>\n ) : null}\n <div className=\"relative flex min-h-0 flex-1 items-center justify-center overflow-auto bg-muted/20 p-4\">\n {!isSupported\n ? renderError(unsupportedText)\n : displayedError\n ? renderError()\n : null}\n {isSupported && !displayedError ? (\n <>\n {isLoading ? renderLoading() : null}\n <img\n {...imageProps}\n alt={alt}\n className={cn(\n 'max-h-full max-w-full transition-transform',\n isLoading ? 'invisible absolute' : 'visible',\n imageClassName\n )}\n onError={() => {\n const nextError = new Error(errorText);\n setIsImageLoading(false);\n setImageError(nextError);\n onError?.(nextError);\n }}\n onLoad={() => {\n setIsImageLoading(false);\n setImageError(null);\n onLoad?.();\n }}\n src={src}\n decoding=\"async\"\n style={{\n objectFit,\n transform: `scale(${scale}) rotate(${rotation}deg)`,\n transformOrigin: 'center center',\n ...style,\n }}\n />\n </>\n ) : null}\n </div>\n </div>\n );\n\n if (Card) {\n return (\n <Card className={className}>\n {CardContent ? <CardContent>{body}</CardContent> : body}\n </Card>\n );\n }\n\n return <div className={className}>{body}</div>;\n}\n"],"names":["SUPPORTED_IMAGE_EXTENSIONS","SUPPORTED_IMAGE_MIME_TYPES","normalizeError","error","clamp","value","min","max","ImageReader","src","alt","fileName","mimeType","components","loading","className","containerClassName","imageClassName","toolbarClassName","loadingText","errorText","unsupportedText","showToolbar","showOpenInNewTab","objectFit","initialScale","minScale","maxScale","scaleStep","controlledScale","onScaleChange","controlledRotation","onRotationChange","allowUnsupportedFormat","supportedExtensions","supportedMimeTypes","onLoad","onError","style","imageProps","Card","CardContent","Button","Skeleton","internalScale","setInternalScale","useState","internalRotation","setInternalRotation","isImageLoading","setIsImageLoading","imageError","setImageError","scale","rotation","displayedError","isSupported","isSupportedMediaSource","isLoading","setNextScale","useCallback","nextScale","clampedScale","setNextRotation","nextRotation","normalizedRotation","formatLabel","useMemo","extension","getMediaExtension","useEffect","renderButton","label","icon","onClick","disabled","buttonClassName","jsx","renderLoading","jsxs","Fragment","renderError","message","body","cn","ZoomOutIcon","ZoomInIcon","RotateCcwIcon","RotateCwIcon","ExternalLinkIcon","nextError"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./utils-IjLH3w2e.cjs"),e=require("./AuthPanel-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./utils-IjLH3w2e.cjs"),e=require("./AuthPanel-CTKx618F.cjs");exports.cn=t.cn;exports.AuthLoginPanel=e.AuthLoginPanel;exports.AuthPageShell=e.AuthPageShell;exports.AuthPanel=e.AuthPanel;exports.AuthRegisterPanel=e.AuthRegisterPanel;exports.AuthVisualCarousel=e.AuthVisualCarousel;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED