@plugable-io/react 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,708 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ Dropzone: () => Dropzone,
34
+ FileImage: () => FileImage,
35
+ FileList: () => FileList,
36
+ FilePreview: () => FilePreview,
37
+ PlugableProvider: () => PlugableProvider,
38
+ clearImageCache: () => clearImageCache,
39
+ useFiles: () => useFiles,
40
+ usePlugable: () => usePlugable
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+
44
+ // src/PlugableProvider.tsx
45
+ var import_react = require("react");
46
+ var import_js = require("@plugable-io/js");
47
+ var import_jsx_runtime = require("react/jsx-runtime");
48
+ var PlugableContext = (0, import_react.createContext)(null);
49
+ function createAuthTokenGetter(authProvider, clerkJWTTemplate) {
50
+ switch (authProvider) {
51
+ case "clerk":
52
+ return async () => {
53
+ if (typeof window !== "undefined" && window.Clerk) {
54
+ const session = await window.Clerk.session;
55
+ if (!session) {
56
+ throw new Error("No active Clerk session found");
57
+ }
58
+ const template = clerkJWTTemplate || void 0;
59
+ return await session.getToken({ template });
60
+ }
61
+ throw new Error(
62
+ "Clerk not found. Please ensure @clerk/clerk-react is installed and ClerkProvider wraps your app, or provide a custom getToken function."
63
+ );
64
+ };
65
+ case "supabase":
66
+ return async () => {
67
+ if (typeof window !== "undefined" && window.supabase) {
68
+ const { data, error } = await window.supabase.auth.getSession();
69
+ if (error || !data.session) {
70
+ throw new Error("No active Supabase session found");
71
+ }
72
+ return data.session.access_token;
73
+ }
74
+ throw new Error(
75
+ "Supabase client not found. Please ensure @supabase/supabase-js is installed and initialized, or provide a custom getToken function."
76
+ );
77
+ };
78
+ case "firebase":
79
+ return async () => {
80
+ if (typeof window !== "undefined" && window.firebase?.auth) {
81
+ const user = window.firebase.auth().currentUser;
82
+ if (!user) {
83
+ throw new Error("No active Firebase user found");
84
+ }
85
+ return await user.getIdToken();
86
+ }
87
+ throw new Error(
88
+ "Firebase not found. Please ensure firebase is installed and initialized, or provide a custom getToken function."
89
+ );
90
+ };
91
+ default:
92
+ throw new Error(
93
+ `Unknown auth provider: ${authProvider}. Please provide either a valid authProvider or a custom getToken function.`
94
+ );
95
+ }
96
+ }
97
+ function PlugableProvider({
98
+ bucketId,
99
+ children,
100
+ getToken,
101
+ authProvider,
102
+ clerkJWTTemplate,
103
+ baseUrl
104
+ }) {
105
+ const listenersRef = (0, import_react.useRef)({});
106
+ const client = (0, import_react.useMemo)(() => {
107
+ if (!getToken && !authProvider) {
108
+ throw new Error(
109
+ "Either getToken function or authProvider must be provided to PlugableProvider"
110
+ );
111
+ }
112
+ const tokenGetter = getToken || createAuthTokenGetter(authProvider, clerkJWTTemplate);
113
+ const client_ = new import_js.BucketClient({
114
+ bucketId,
115
+ getToken: tokenGetter,
116
+ baseUrl
117
+ });
118
+ return { client: client_, tokenGetter };
119
+ }, [bucketId, getToken, authProvider, clerkJWTTemplate, baseUrl]);
120
+ const on = (0, import_react.useCallback)((event, handler) => {
121
+ if (!listenersRef.current[event]) {
122
+ listenersRef.current[event] = /* @__PURE__ */ new Set();
123
+ }
124
+ listenersRef.current[event].add(handler);
125
+ return () => {
126
+ listenersRef.current[event]?.delete(handler);
127
+ };
128
+ }, []);
129
+ const emit = (0, import_react.useCallback)((event, data) => {
130
+ listenersRef.current[event]?.forEach((handler) => handler(data));
131
+ }, []);
132
+ const value = (0, import_react.useMemo)(
133
+ () => ({
134
+ client: client.client,
135
+ bucketId,
136
+ on,
137
+ emit,
138
+ getToken: client.tokenGetter,
139
+ baseUrl
140
+ }),
141
+ [client, bucketId, on, emit, baseUrl]
142
+ );
143
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlugableContext.Provider, { value, children });
144
+ }
145
+ function usePlugable() {
146
+ const context = (0, import_react.useContext)(PlugableContext);
147
+ if (!context) {
148
+ throw new Error("usePlugable must be used within a PlugableProvider");
149
+ }
150
+ return context;
151
+ }
152
+
153
+ // src/components/Dropzone.tsx
154
+ var import_react2 = __toESM(require("react"));
155
+ var import_js2 = require("@plugable-io/js");
156
+ var import_jsx_runtime2 = require("react/jsx-runtime");
157
+ function Dropzone({
158
+ bucketId: _bucketId,
159
+ metadata,
160
+ onUploadComplete,
161
+ onUploadError,
162
+ onProgressUpdate,
163
+ accept,
164
+ maxFiles,
165
+ children,
166
+ className,
167
+ style
168
+ }) {
169
+ const { client: defaultClient, emit, getToken, baseUrl } = usePlugable();
170
+ const client = import_react2.default.useMemo(() => {
171
+ if (_bucketId) {
172
+ return new import_js2.BucketClient({
173
+ bucketId: _bucketId,
174
+ getToken,
175
+ baseUrl
176
+ });
177
+ }
178
+ return defaultClient;
179
+ }, [_bucketId, defaultClient, getToken, baseUrl]);
180
+ const [isDragActive, setIsDragActive] = (0, import_react2.useState)(false);
181
+ const [isUploading, setIsUploading] = (0, import_react2.useState)(false);
182
+ const [uploadProgress, setUploadProgress] = (0, import_react2.useState)({});
183
+ const [uploadedFiles, setUploadedFiles] = (0, import_react2.useState)([]);
184
+ const fileInputRef = import_react2.default.useRef(null);
185
+ const uploadFiles = (0, import_react2.useCallback)(
186
+ async (files) => {
187
+ const fileArray = Array.from(files);
188
+ const filesToUpload = maxFiles ? fileArray.slice(0, maxFiles) : fileArray;
189
+ setIsUploading(true);
190
+ setUploadProgress({});
191
+ const uploadPromises = filesToUpload.map(async (file) => {
192
+ const options = {
193
+ metadata,
194
+ onProgress: (progress) => {
195
+ setUploadProgress((prev) => ({
196
+ ...prev,
197
+ [file.name]: progress
198
+ }));
199
+ onProgressUpdate?.(file.name, progress);
200
+ }
201
+ };
202
+ try {
203
+ const uploadedFile = await client.upload(file, options);
204
+ return uploadedFile;
205
+ } catch (error) {
206
+ console.error(`Failed to upload ${file.name}:`, error);
207
+ onUploadError?.(error);
208
+ return null;
209
+ }
210
+ });
211
+ const results = await Promise.all(uploadPromises);
212
+ const successfulUploads = results.filter((f) => f !== null);
213
+ setUploadedFiles((prev) => [...prev, ...successfulUploads]);
214
+ setIsUploading(false);
215
+ setUploadProgress({});
216
+ if (successfulUploads.length > 0) {
217
+ successfulUploads.forEach((file) => emit("file.uploaded", file));
218
+ onUploadComplete?.(successfulUploads);
219
+ }
220
+ },
221
+ [client, metadata, maxFiles, onUploadComplete, onUploadError, onProgressUpdate, emit]
222
+ );
223
+ const handleDrop = (0, import_react2.useCallback)(
224
+ (e) => {
225
+ e.preventDefault();
226
+ e.stopPropagation();
227
+ setIsDragActive(false);
228
+ if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
229
+ uploadFiles(e.dataTransfer.files);
230
+ }
231
+ },
232
+ [uploadFiles]
233
+ );
234
+ const handleDragOver = (0, import_react2.useCallback)((e) => {
235
+ e.preventDefault();
236
+ e.stopPropagation();
237
+ setIsDragActive(true);
238
+ }, []);
239
+ const handleDragLeave = (0, import_react2.useCallback)((e) => {
240
+ e.preventDefault();
241
+ e.stopPropagation();
242
+ setIsDragActive(false);
243
+ }, []);
244
+ const handleFileInputChange = (0, import_react2.useCallback)(
245
+ (e) => {
246
+ if (e.target.files && e.target.files.length > 0) {
247
+ uploadFiles(e.target.files);
248
+ }
249
+ },
250
+ [uploadFiles]
251
+ );
252
+ const openFileDialog = (0, import_react2.useCallback)(() => {
253
+ fileInputRef.current?.click();
254
+ }, []);
255
+ const renderProps = {
256
+ isDragActive,
257
+ isUploading,
258
+ uploadProgress,
259
+ openFileDialog,
260
+ uploadedFiles
261
+ };
262
+ if (children) {
263
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
264
+ "div",
265
+ {
266
+ onDrop: handleDrop,
267
+ onDragOver: handleDragOver,
268
+ onDragLeave: handleDragLeave,
269
+ className,
270
+ style,
271
+ children: [
272
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
273
+ "input",
274
+ {
275
+ ref: fileInputRef,
276
+ type: "file",
277
+ multiple: !maxFiles || maxFiles > 1,
278
+ accept,
279
+ onChange: handleFileInputChange,
280
+ style: { display: "none" }
281
+ }
282
+ ),
283
+ children(renderProps)
284
+ ]
285
+ }
286
+ );
287
+ }
288
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
289
+ "div",
290
+ {
291
+ onDrop: handleDrop,
292
+ onDragOver: handleDragOver,
293
+ onDragLeave: handleDragLeave,
294
+ onClick: openFileDialog,
295
+ className,
296
+ style: {
297
+ border: `2px dashed ${isDragActive ? "#0070f3" : "#ccc"}`,
298
+ borderRadius: "8px",
299
+ padding: "40px 20px",
300
+ textAlign: "center",
301
+ cursor: "pointer",
302
+ backgroundColor: isDragActive ? "#f0f8ff" : "#fafafa",
303
+ transition: "all 0.2s ease",
304
+ ...style
305
+ },
306
+ children: [
307
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
308
+ "input",
309
+ {
310
+ ref: fileInputRef,
311
+ type: "file",
312
+ multiple: !maxFiles || maxFiles > 1,
313
+ accept,
314
+ onChange: handleFileInputChange,
315
+ style: { display: "none" }
316
+ }
317
+ ),
318
+ isUploading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { margin: 0, fontWeight: "bold", color: "#333" }, children: "Uploading..." }),
320
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "16px" }, children: Object.entries(uploadProgress).map(([fileName, progress]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "8px" }, children: [
321
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { fontSize: "14px", color: "#666", marginBottom: "4px" }, children: [
322
+ fileName,
323
+ ": ",
324
+ progress,
325
+ "%"
326
+ ] }),
327
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
328
+ "div",
329
+ {
330
+ style: {
331
+ width: "100%",
332
+ height: "8px",
333
+ backgroundColor: "#e0e0e0",
334
+ borderRadius: "4px",
335
+ overflow: "hidden"
336
+ },
337
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
338
+ "div",
339
+ {
340
+ style: {
341
+ width: `${progress}%`,
342
+ height: "100%",
343
+ backgroundColor: "#0070f3",
344
+ transition: "width 0.3s ease"
345
+ }
346
+ }
347
+ )
348
+ }
349
+ )
350
+ ] }, fileName)) })
351
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
352
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { margin: 0, fontWeight: "bold", fontSize: "16px", color: "#333" }, children: isDragActive ? "Drop files here" : "Drag and drop files here" }),
353
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { margin: "8px 0 0 0", fontSize: "14px", color: "#666" }, children: "or click to select files" }),
354
+ maxFiles && maxFiles > 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: { margin: "4px 0 0 0", fontSize: "12px", color: "#999" }, children: [
355
+ "(Maximum ",
356
+ maxFiles,
357
+ " files)"
358
+ ] })
359
+ ] })
360
+ ]
361
+ }
362
+ );
363
+ }
364
+
365
+ // src/hooks/useFiles.ts
366
+ var import_react3 = require("react");
367
+ function useFiles({
368
+ metadata,
369
+ startPage = 1,
370
+ perPage = 20,
371
+ mediaType,
372
+ autoLoad = true,
373
+ orderBy,
374
+ orderDirection
375
+ } = {}) {
376
+ const { client, on } = usePlugable();
377
+ const [files, setFiles] = (0, import_react3.useState)([]);
378
+ const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
379
+ const [page, setPage] = (0, import_react3.useState)(startPage);
380
+ const [hasNext, setHasNext] = (0, import_react3.useState)(false);
381
+ const metadataKey = JSON.stringify(metadata);
382
+ const stableMetadata = (0, import_react3.useMemo)(() => metadata, [metadataKey]);
383
+ const loadedParamsRef = (0, import_react3.useRef)(null);
384
+ const paramsKeyWithPage = (0, import_react3.useMemo)(() => JSON.stringify({
385
+ metadata: stableMetadata,
386
+ mediaType,
387
+ perPage,
388
+ orderBy,
389
+ orderDirection,
390
+ page
391
+ }), [stableMetadata, mediaType, perPage, orderBy, orderDirection, page]);
392
+ const fetchFiles = (0, import_react3.useCallback)(async (pageNum) => {
393
+ setIsLoading(true);
394
+ try {
395
+ const options = {
396
+ metadata: stableMetadata,
397
+ media_type: mediaType,
398
+ page: pageNum,
399
+ per_page: perPage,
400
+ with_download_url: true,
401
+ order_by: orderBy,
402
+ order_direction: orderDirection
403
+ };
404
+ const response = await client.list(options);
405
+ setFiles(response.files);
406
+ setHasNext(response.paging.has_next_page);
407
+ const currentParamsKey = JSON.stringify({
408
+ metadata: stableMetadata,
409
+ mediaType,
410
+ perPage,
411
+ orderBy,
412
+ orderDirection,
413
+ page: pageNum
414
+ });
415
+ loadedParamsRef.current = currentParamsKey;
416
+ } catch (err) {
417
+ console.error("Failed to load files:", err);
418
+ setFiles([]);
419
+ setHasNext(false);
420
+ } finally {
421
+ setIsLoading(false);
422
+ }
423
+ }, [client, stableMetadata, mediaType, perPage, orderBy, orderDirection]);
424
+ (0, import_react3.useEffect)(() => {
425
+ const hasLoadedForParams = loadedParamsRef.current === paramsKeyWithPage;
426
+ const shouldLoad = autoLoad || !hasLoadedForParams && files.length === 0 && !isLoading;
427
+ if (shouldLoad) {
428
+ fetchFiles(page);
429
+ }
430
+ }, [fetchFiles, page, autoLoad, paramsKeyWithPage, isLoading]);
431
+ (0, import_react3.useEffect)(() => {
432
+ const unsubscribe = on("file.uploaded", () => {
433
+ fetchFiles(page);
434
+ });
435
+ return unsubscribe;
436
+ }, [on, fetchFiles, page]);
437
+ const loadNextPage = (0, import_react3.useCallback)(() => {
438
+ if (hasNext) {
439
+ setPage((p) => p + 1);
440
+ }
441
+ }, [hasNext]);
442
+ const loadPreviousPage = (0, import_react3.useCallback)(() => {
443
+ setPage((p) => Math.max(1, p - 1));
444
+ }, []);
445
+ const refresh = (0, import_react3.useCallback)(async () => {
446
+ await fetchFiles(page);
447
+ }, [fetchFiles, page]);
448
+ return {
449
+ files,
450
+ isLoading,
451
+ pagination: {
452
+ current: page,
453
+ hasNext,
454
+ hasPrevious: page > 1,
455
+ loadNextPage,
456
+ loadPreviousPage
457
+ },
458
+ setPage,
459
+ refresh
460
+ };
461
+ }
462
+
463
+ // src/components/FileList.tsx
464
+ var import_jsx_runtime3 = require("react/jsx-runtime");
465
+ function FileList({
466
+ metadata,
467
+ mediaType,
468
+ perPage = 20,
469
+ autoLoad = true,
470
+ startPage = 1,
471
+ children
472
+ }) {
473
+ const { files, isLoading, pagination, refresh } = useFiles({
474
+ metadata,
475
+ mediaType,
476
+ perPage,
477
+ autoLoad,
478
+ startPage
479
+ });
480
+ const renderProps = {
481
+ files,
482
+ isLoading,
483
+ hasMore: pagination.hasNext,
484
+ loadMore: pagination.loadNextPage,
485
+ refresh,
486
+ error: null,
487
+ pagination
488
+ };
489
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: children(renderProps) });
490
+ }
491
+
492
+ // src/components/FileImage.tsx
493
+ var import_react4 = require("react");
494
+ var import_jsx_runtime4 = require("react/jsx-runtime");
495
+ var imageCache = /* @__PURE__ */ new Map();
496
+ function FileImage({
497
+ file,
498
+ width,
499
+ height,
500
+ objectFit = "cover",
501
+ borderRadius,
502
+ alt,
503
+ className,
504
+ style,
505
+ onLoad,
506
+ onError
507
+ }) {
508
+ const [imageSrc, setImageSrc] = (0, import_react4.useState)(null);
509
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(true);
510
+ const [error, setError] = (0, import_react4.useState)(null);
511
+ (0, import_react4.useEffect)(() => {
512
+ let isMounted = true;
513
+ let objectUrl = null;
514
+ const loadImage = async () => {
515
+ try {
516
+ const cacheKey = `${file.id}-${file.checksum}`;
517
+ const cached = imageCache.get(cacheKey);
518
+ if (cached) {
519
+ if (isMounted) {
520
+ setImageSrc(cached);
521
+ setIsLoading(false);
522
+ }
523
+ return;
524
+ }
525
+ if (file.download_url) {
526
+ const response = await fetch(file.download_url, {
527
+ headers: {
528
+ "Cache-Control": "private, max-age=31536000",
529
+ "If-None-Match": file.checksum
530
+ }
531
+ });
532
+ if (!response.ok) {
533
+ throw new Error(`Failed to fetch image: ${response.statusText}`);
534
+ }
535
+ const blob = await response.blob();
536
+ objectUrl = URL.createObjectURL(blob);
537
+ imageCache.set(cacheKey, objectUrl);
538
+ if (isMounted) {
539
+ setImageSrc(objectUrl);
540
+ setIsLoading(false);
541
+ }
542
+ } else {
543
+ throw new Error("No download URL available for file");
544
+ }
545
+ } catch (err) {
546
+ const error2 = err;
547
+ if (isMounted) {
548
+ setError(error2);
549
+ setIsLoading(false);
550
+ onError?.(error2);
551
+ }
552
+ console.error("Failed to load image:", err);
553
+ }
554
+ };
555
+ loadImage();
556
+ return () => {
557
+ isMounted = false;
558
+ };
559
+ }, [file.id, file.checksum, file.download_url, onError]);
560
+ const handleLoad = () => {
561
+ setIsLoading(false);
562
+ onLoad?.();
563
+ };
564
+ const handleError = () => {
565
+ const err = new Error("Image failed to load");
566
+ setError(err);
567
+ setIsLoading(false);
568
+ onError?.(err);
569
+ };
570
+ const imageStyle = {
571
+ width: width || "100%",
572
+ height: height || "auto",
573
+ objectFit,
574
+ borderRadius: borderRadius || 0,
575
+ ...style
576
+ };
577
+ if (error) {
578
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
579
+ "div",
580
+ {
581
+ className,
582
+ style: {
583
+ ...imageStyle,
584
+ display: "flex",
585
+ alignItems: "center",
586
+ justifyContent: "center",
587
+ backgroundColor: "#f0f0f0",
588
+ color: "#999",
589
+ fontSize: "14px"
590
+ },
591
+ children: "Failed to load image"
592
+ }
593
+ );
594
+ }
595
+ if (isLoading || !imageSrc) {
596
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
597
+ "div",
598
+ {
599
+ className,
600
+ style: {
601
+ ...imageStyle,
602
+ display: "flex",
603
+ alignItems: "center",
604
+ justifyContent: "center",
605
+ backgroundColor: "#f0f0f0"
606
+ },
607
+ children: [
608
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
609
+ "div",
610
+ {
611
+ style: {
612
+ width: "40px",
613
+ height: "40px",
614
+ border: "3px solid #e0e0e0",
615
+ borderTop: "3px solid #0070f3",
616
+ borderRadius: "50%",
617
+ animation: "spin 1s linear infinite"
618
+ }
619
+ }
620
+ ),
621
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
622
+ @keyframes spin {
623
+ 0% { transform: rotate(0deg); }
624
+ 100% { transform: rotate(360deg); }
625
+ }
626
+ ` })
627
+ ]
628
+ }
629
+ );
630
+ }
631
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
632
+ "img",
633
+ {
634
+ src: imageSrc,
635
+ alt: alt || file.name,
636
+ className,
637
+ style: imageStyle,
638
+ onLoad: handleLoad,
639
+ onError: handleError
640
+ }
641
+ );
642
+ }
643
+ function clearImageCache() {
644
+ imageCache.forEach((url) => URL.revokeObjectURL(url));
645
+ imageCache.clear();
646
+ }
647
+
648
+ // src/components/FilePreview.tsx
649
+ var import_jsx_runtime5 = require("react/jsx-runtime");
650
+ function FilePreview({
651
+ file,
652
+ width = 80,
653
+ height = 80,
654
+ className,
655
+ style,
656
+ objectFit = "cover",
657
+ showExtension = true,
658
+ renderNonImage
659
+ }) {
660
+ const isImage = file.content_type.startsWith("image/");
661
+ const containerStyle = {
662
+ width,
663
+ height,
664
+ borderRadius: 4,
665
+ overflow: "hidden",
666
+ backgroundColor: "#f5f5f5",
667
+ display: "flex",
668
+ alignItems: "center",
669
+ justifyContent: "center",
670
+ border: "1px solid #eee",
671
+ ...style
672
+ };
673
+ if (isImage) {
674
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
675
+ FileImage,
676
+ {
677
+ file,
678
+ width,
679
+ height,
680
+ objectFit,
681
+ className,
682
+ style,
683
+ borderRadius: 4
684
+ }
685
+ );
686
+ }
687
+ if (renderNonImage) {
688
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, style: containerStyle, children: renderNonImage(file) });
689
+ }
690
+ const extension = file.name.split(".").pop()?.toUpperCase() || "FILE";
691
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, style: containerStyle, children: showExtension && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: {
692
+ fontSize: "12px",
693
+ fontWeight: "bold",
694
+ color: "#666",
695
+ textTransform: "uppercase"
696
+ }, children: extension }) });
697
+ }
698
+ // Annotate the CommonJS export names for ESM import in node:
699
+ 0 && (module.exports = {
700
+ Dropzone,
701
+ FileImage,
702
+ FileList,
703
+ FilePreview,
704
+ PlugableProvider,
705
+ clearImageCache,
706
+ useFiles,
707
+ usePlugable
708
+ });