@plugable-io/react 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -0
- package/dist/index.d.mts +32 -3
- package/dist/index.d.ts +32 -3
- package/dist/index.js +389 -99
- package/dist/index.mjs +395 -108
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,7 +35,10 @@ __export(index_exports, {
|
|
|
35
35
|
FileList: () => FileList,
|
|
36
36
|
FilePreview: () => FilePreview,
|
|
37
37
|
PlugableProvider: () => PlugableProvider,
|
|
38
|
+
applyCSSVariables: () => applyCSSVariables,
|
|
38
39
|
clearImageCache: () => clearImageCache,
|
|
40
|
+
generateCSSVariables: () => generateCSSVariables,
|
|
41
|
+
getThemeColors: () => getThemeColors,
|
|
39
42
|
useFiles: () => useFiles,
|
|
40
43
|
usePlugable: () => usePlugable
|
|
41
44
|
});
|
|
@@ -44,6 +47,112 @@ module.exports = __toCommonJS(index_exports);
|
|
|
44
47
|
// src/PlugableProvider.tsx
|
|
45
48
|
var import_react = require("react");
|
|
46
49
|
var import_js = require("@plugable-io/js");
|
|
50
|
+
|
|
51
|
+
// src/utils/theme.ts
|
|
52
|
+
var defaultLightTheme = {
|
|
53
|
+
// Purple to blue accent gradient
|
|
54
|
+
accentPrimary: "#9333ea",
|
|
55
|
+
// purple-600
|
|
56
|
+
accentSecondary: "#2563eb",
|
|
57
|
+
// blue-600
|
|
58
|
+
accentHover: "#7c3aed",
|
|
59
|
+
// purple-700
|
|
60
|
+
// Light backgrounds
|
|
61
|
+
baseBg: "#ffffff",
|
|
62
|
+
baseSurface: "#f8fafc",
|
|
63
|
+
// slate-50
|
|
64
|
+
baseBorder: "#e2e8f0",
|
|
65
|
+
// slate-200
|
|
66
|
+
// Dark text on light
|
|
67
|
+
textPrimary: "#0f172a",
|
|
68
|
+
// slate-900
|
|
69
|
+
textSecondary: "#475569",
|
|
70
|
+
// slate-600
|
|
71
|
+
textMuted: "#94a3b8",
|
|
72
|
+
// slate-400
|
|
73
|
+
// State colors
|
|
74
|
+
success: "#10b981",
|
|
75
|
+
// green-500
|
|
76
|
+
error: "#ef4444",
|
|
77
|
+
// red-500
|
|
78
|
+
warning: "#f59e0b",
|
|
79
|
+
// amber-500
|
|
80
|
+
// Overlay
|
|
81
|
+
overlay: "rgba(0, 0, 0, 0.05)",
|
|
82
|
+
backdropBlur: "blur(12px)"
|
|
83
|
+
};
|
|
84
|
+
var defaultDarkTheme = {
|
|
85
|
+
// Purple to blue accent gradient (same as light)
|
|
86
|
+
accentPrimary: "#9333ea",
|
|
87
|
+
accentSecondary: "#2563eb",
|
|
88
|
+
accentHover: "#a855f7",
|
|
89
|
+
// purple-500 (lighter for dark mode)
|
|
90
|
+
// Dark backgrounds
|
|
91
|
+
baseBg: "#0f172a",
|
|
92
|
+
// slate-900
|
|
93
|
+
baseSurface: "rgba(30, 41, 59, 0.5)",
|
|
94
|
+
// slate-800/50 with transparency
|
|
95
|
+
baseBorder: "rgba(255, 255, 255, 0.1)",
|
|
96
|
+
// Light text on dark
|
|
97
|
+
textPrimary: "#f1f5f9",
|
|
98
|
+
// slate-100
|
|
99
|
+
textSecondary: "#cbd5e1",
|
|
100
|
+
// slate-300
|
|
101
|
+
textMuted: "#64748b",
|
|
102
|
+
// slate-500
|
|
103
|
+
// State colors (slightly adjusted for dark mode)
|
|
104
|
+
success: "#34d399",
|
|
105
|
+
// green-400
|
|
106
|
+
error: "#f87171",
|
|
107
|
+
// red-400
|
|
108
|
+
warning: "#fbbf24",
|
|
109
|
+
// amber-400
|
|
110
|
+
// Overlay
|
|
111
|
+
overlay: "rgba(0, 0, 0, 0.3)",
|
|
112
|
+
backdropBlur: "blur(24px)"
|
|
113
|
+
};
|
|
114
|
+
function getThemeColors(config = {}) {
|
|
115
|
+
const { theme = "dark", accentColor, baseColor } = config;
|
|
116
|
+
const isDark = theme === "dark" || theme === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
117
|
+
const colors = isDark ? { ...defaultDarkTheme } : { ...defaultLightTheme };
|
|
118
|
+
if (accentColor) {
|
|
119
|
+
colors.accentPrimary = accentColor;
|
|
120
|
+
colors.accentSecondary = accentColor;
|
|
121
|
+
colors.accentHover = accentColor;
|
|
122
|
+
}
|
|
123
|
+
if (baseColor) {
|
|
124
|
+
colors.baseBg = baseColor;
|
|
125
|
+
colors.baseSurface = baseColor;
|
|
126
|
+
}
|
|
127
|
+
return colors;
|
|
128
|
+
}
|
|
129
|
+
function generateCSSVariables(colors) {
|
|
130
|
+
return {
|
|
131
|
+
"--plugable-accent-primary": colors.accentPrimary,
|
|
132
|
+
"--plugable-accent-secondary": colors.accentSecondary,
|
|
133
|
+
"--plugable-accent-hover": colors.accentHover,
|
|
134
|
+
"--plugable-base-bg": colors.baseBg,
|
|
135
|
+
"--plugable-base-surface": colors.baseSurface,
|
|
136
|
+
"--plugable-base-border": colors.baseBorder,
|
|
137
|
+
"--plugable-text-primary": colors.textPrimary,
|
|
138
|
+
"--plugable-text-secondary": colors.textSecondary,
|
|
139
|
+
"--plugable-text-muted": colors.textMuted,
|
|
140
|
+
"--plugable-success": colors.success,
|
|
141
|
+
"--plugable-error": colors.error,
|
|
142
|
+
"--plugable-warning": colors.warning,
|
|
143
|
+
"--plugable-overlay": colors.overlay,
|
|
144
|
+
"--plugable-backdrop-blur": colors.backdropBlur
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function applyCSSVariables(element, config = {}) {
|
|
148
|
+
const colors = getThemeColors(config);
|
|
149
|
+
const variables = generateCSSVariables(colors);
|
|
150
|
+
Object.entries(variables).forEach(([key, value]) => {
|
|
151
|
+
element.style.setProperty(key, value);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/PlugableProvider.tsx
|
|
47
156
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
48
157
|
var PlugableContext = (0, import_react.createContext)(null);
|
|
49
158
|
function createAuthTokenGetter(authProvider, clerkJWTTemplate) {
|
|
@@ -88,6 +197,13 @@ function createAuthTokenGetter(authProvider, clerkJWTTemplate) {
|
|
|
88
197
|
"Firebase not found. Please ensure firebase is installed and initialized, or provide a custom getToken function."
|
|
89
198
|
);
|
|
90
199
|
};
|
|
200
|
+
case "generic_jwks":
|
|
201
|
+
case "generic_jwt":
|
|
202
|
+
return async () => {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`Manual token required for ${authProvider}. Please provide a custom getToken function.`
|
|
205
|
+
);
|
|
206
|
+
};
|
|
91
207
|
default:
|
|
92
208
|
throw new Error(
|
|
93
209
|
`Unknown auth provider: ${authProvider}. Please provide either a valid authProvider or a custom getToken function.`
|
|
@@ -101,11 +217,15 @@ function PlugableProvider({
|
|
|
101
217
|
authProvider,
|
|
102
218
|
clerkJWTTemplate,
|
|
103
219
|
baseUrl,
|
|
104
|
-
staleTime = 5 * 60 * 1e3
|
|
220
|
+
staleTime = 5 * 60 * 1e3,
|
|
105
221
|
// Default 5 minutes
|
|
222
|
+
accentColor,
|
|
223
|
+
baseColor,
|
|
224
|
+
theme = "dark"
|
|
106
225
|
}) {
|
|
107
226
|
const listenersRef = (0, import_react.useRef)({});
|
|
108
227
|
const [cache, setCacheState] = (0, import_react.useState)(/* @__PURE__ */ new Map());
|
|
228
|
+
const containerRef = (0, import_react.useRef)(null);
|
|
109
229
|
const client = (0, import_react.useMemo)(() => {
|
|
110
230
|
if (!getToken && !authProvider) {
|
|
111
231
|
throw new Error(
|
|
@@ -175,7 +295,12 @@ function PlugableProvider({
|
|
|
175
295
|
}),
|
|
176
296
|
[client, bucketId, on, emit, baseUrl, staleTime, getCache, setCache, invalidateCache]
|
|
177
297
|
);
|
|
178
|
-
|
|
298
|
+
(0, import_react.useEffect)(() => {
|
|
299
|
+
if (containerRef.current) {
|
|
300
|
+
applyCSSVariables(containerRef.current, { accentColor, baseColor, theme });
|
|
301
|
+
}
|
|
302
|
+
}, [accentColor, baseColor, theme]);
|
|
303
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlugableContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: containerRef, className: "plugable-root", children }) });
|
|
179
304
|
}
|
|
180
305
|
function usePlugable() {
|
|
181
306
|
const context = (0, import_react.useContext)(PlugableContext);
|
|
@@ -320,6 +445,74 @@ function Dropzone({
|
|
|
320
445
|
}
|
|
321
446
|
);
|
|
322
447
|
}
|
|
448
|
+
const containerStyle = {
|
|
449
|
+
position: "relative",
|
|
450
|
+
border: `2px dashed ${isDragActive ? "var(--plugable-accent-primary)" : "var(--plugable-base-border)"}`,
|
|
451
|
+
borderRadius: "12px",
|
|
452
|
+
padding: "48px 24px",
|
|
453
|
+
textAlign: "center",
|
|
454
|
+
cursor: "pointer",
|
|
455
|
+
background: isDragActive ? "var(--plugable-accent-primary)10" : "var(--plugable-base-surface)",
|
|
456
|
+
backdropFilter: "var(--plugable-backdrop-blur)",
|
|
457
|
+
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
458
|
+
...style
|
|
459
|
+
};
|
|
460
|
+
const iconContainerStyle = {
|
|
461
|
+
width: "48px",
|
|
462
|
+
height: "48px",
|
|
463
|
+
margin: "0 auto 16px",
|
|
464
|
+
borderRadius: "50%",
|
|
465
|
+
display: "flex",
|
|
466
|
+
alignItems: "center",
|
|
467
|
+
justifyContent: "center",
|
|
468
|
+
background: isDragActive ? `linear-gradient(135deg, var(--plugable-accent-primary), var(--plugable-accent-secondary))` : "var(--plugable-overlay)",
|
|
469
|
+
transition: "all 0.3s ease",
|
|
470
|
+
transform: isDragActive ? "scale(1.1)" : "scale(1)"
|
|
471
|
+
};
|
|
472
|
+
const cloudIconStyle = {
|
|
473
|
+
width: "24px",
|
|
474
|
+
height: "24px",
|
|
475
|
+
color: isDragActive ? "#fff" : "var(--plugable-accent-primary)"
|
|
476
|
+
};
|
|
477
|
+
const titleStyle = {
|
|
478
|
+
margin: 0,
|
|
479
|
+
fontWeight: 600,
|
|
480
|
+
fontSize: "16px",
|
|
481
|
+
color: "var(--plugable-text-primary)",
|
|
482
|
+
marginBottom: "8px"
|
|
483
|
+
};
|
|
484
|
+
const subtitleStyle = {
|
|
485
|
+
margin: 0,
|
|
486
|
+
fontSize: "14px",
|
|
487
|
+
color: "var(--plugable-text-secondary)"
|
|
488
|
+
};
|
|
489
|
+
const maxFilesStyle = {
|
|
490
|
+
margin: "4px 0 0 0",
|
|
491
|
+
fontSize: "12px",
|
|
492
|
+
color: "var(--plugable-text-muted)"
|
|
493
|
+
};
|
|
494
|
+
const progressContainerStyle = {
|
|
495
|
+
marginTop: "16px"
|
|
496
|
+
};
|
|
497
|
+
const progressItemStyle = {
|
|
498
|
+
marginBottom: "12px",
|
|
499
|
+
textAlign: "left"
|
|
500
|
+
};
|
|
501
|
+
const progressLabelStyle = {
|
|
502
|
+
fontSize: "14px",
|
|
503
|
+
color: "var(--plugable-text-secondary)",
|
|
504
|
+
marginBottom: "6px",
|
|
505
|
+
display: "flex",
|
|
506
|
+
justifyContent: "space-between",
|
|
507
|
+
alignItems: "center"
|
|
508
|
+
};
|
|
509
|
+
const progressBarBgStyle = {
|
|
510
|
+
width: "100%",
|
|
511
|
+
height: "6px",
|
|
512
|
+
backgroundColor: "var(--plugable-overlay)",
|
|
513
|
+
borderRadius: "3px",
|
|
514
|
+
overflow: "hidden"
|
|
515
|
+
};
|
|
323
516
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
324
517
|
"div",
|
|
325
518
|
{
|
|
@@ -328,16 +521,7 @@ function Dropzone({
|
|
|
328
521
|
onDragLeave: handleDragLeave,
|
|
329
522
|
onClick: openFileDialog,
|
|
330
523
|
className,
|
|
331
|
-
style:
|
|
332
|
-
border: `2px dashed ${isDragActive ? "#0070f3" : "#ccc"}`,
|
|
333
|
-
borderRadius: "8px",
|
|
334
|
-
padding: "40px 20px",
|
|
335
|
-
textAlign: "center",
|
|
336
|
-
cursor: "pointer",
|
|
337
|
-
backgroundColor: isDragActive ? "#f0f8ff" : "#fafafa",
|
|
338
|
-
transition: "all 0.2s ease",
|
|
339
|
-
...style
|
|
340
|
-
},
|
|
524
|
+
style: containerStyle,
|
|
341
525
|
children: [
|
|
342
526
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
343
527
|
"input",
|
|
@@ -351,42 +535,34 @@ function Dropzone({
|
|
|
351
535
|
}
|
|
352
536
|
),
|
|
353
537
|
isUploading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
354
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("
|
|
355
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
":
|
|
359
|
-
|
|
360
|
-
|
|
538
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: iconContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { style: cloudIconStyle, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" }) }) }),
|
|
539
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: titleStyle, children: "Uploading..." }),
|
|
540
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: progressContainerStyle, children: Object.entries(uploadProgress).map(([fileName, progress]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: progressItemStyle, children: [
|
|
541
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: progressLabelStyle, children: [
|
|
542
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: fileName }),
|
|
543
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { fontWeight: 600 }, children: [
|
|
544
|
+
progress,
|
|
545
|
+
"%"
|
|
546
|
+
] })
|
|
361
547
|
] }),
|
|
362
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: progressBarBgStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
363
549
|
"div",
|
|
364
550
|
{
|
|
365
551
|
style: {
|
|
366
|
-
width:
|
|
367
|
-
height: "
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
372
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
373
|
-
"div",
|
|
374
|
-
{
|
|
375
|
-
style: {
|
|
376
|
-
width: `${progress}%`,
|
|
377
|
-
height: "100%",
|
|
378
|
-
backgroundColor: "#0070f3",
|
|
379
|
-
transition: "width 0.3s ease"
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
)
|
|
552
|
+
width: `${progress}%`,
|
|
553
|
+
height: "100%",
|
|
554
|
+
background: `linear-gradient(90deg, var(--plugable-accent-primary), var(--plugable-accent-secondary))`,
|
|
555
|
+
transition: "width 0.3s ease",
|
|
556
|
+
borderRadius: "3px"
|
|
557
|
+
}
|
|
383
558
|
}
|
|
384
|
-
)
|
|
559
|
+
) })
|
|
385
560
|
] }, fileName)) })
|
|
386
561
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
387
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("
|
|
388
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style:
|
|
389
|
-
|
|
562
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: iconContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { style: cloudIconStyle, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" }) }) }),
|
|
563
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: titleStyle, children: isDragActive ? "Drop files here" : "Click to upload or drag and drop" }),
|
|
564
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: subtitleStyle, children: accept ? `Accepted: ${accept}` : "Any file type" }),
|
|
565
|
+
maxFiles && maxFiles > 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: maxFilesStyle, children: [
|
|
390
566
|
"(Maximum ",
|
|
391
567
|
maxFiles,
|
|
392
568
|
" files)"
|
|
@@ -601,29 +777,47 @@ function FileImage({
|
|
|
601
777
|
return;
|
|
602
778
|
}
|
|
603
779
|
if (file.download_url) {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
780
|
+
let response = null;
|
|
781
|
+
try {
|
|
782
|
+
response = await fetch(file.download_url, {
|
|
783
|
+
headers: {
|
|
784
|
+
"Cache-Control": "private, max-age=31536000",
|
|
785
|
+
"If-None-Match": file.checksum
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
if (!response.ok) {
|
|
789
|
+
const downloadUrlKey = `${file.id}-${file.download_url}`;
|
|
790
|
+
if (response.status === 403 && onRefetchNeeded && refetchAttemptedRef.current !== downloadUrlKey) {
|
|
791
|
+
refetchAttemptedRef.current = downloadUrlKey;
|
|
792
|
+
imageCache.delete(cacheKey);
|
|
793
|
+
await onRefetchNeeded();
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
throw new Error(`Failed to fetch image: ${response.statusText}`);
|
|
608
797
|
}
|
|
609
|
-
|
|
610
|
-
|
|
798
|
+
const blob = await response.blob();
|
|
799
|
+
objectUrl = URL.createObjectURL(blob);
|
|
800
|
+
imageCache.set(cacheKey, objectUrl);
|
|
801
|
+
if (isMounted) {
|
|
802
|
+
setImageSrc(objectUrl);
|
|
803
|
+
setIsLoading(false);
|
|
804
|
+
refetchAttemptedRef.current = null;
|
|
805
|
+
}
|
|
806
|
+
} catch (fetchError) {
|
|
611
807
|
const downloadUrlKey = `${file.id}-${file.download_url}`;
|
|
612
|
-
|
|
808
|
+
const isNetworkError = !response && (fetchError.message?.includes("Failed to fetch") || fetchError.message?.includes("CORS") || fetchError.name === "TypeError" || fetchError.message?.includes("network") || fetchError.message?.includes("ERR_FAILED"));
|
|
809
|
+
const is403Error = response?.status === 403;
|
|
810
|
+
if ((isNetworkError || is403Error) && onRefetchNeeded && refetchAttemptedRef.current !== downloadUrlKey) {
|
|
613
811
|
refetchAttemptedRef.current = downloadUrlKey;
|
|
614
812
|
imageCache.delete(cacheKey);
|
|
615
|
-
|
|
616
|
-
|
|
813
|
+
try {
|
|
814
|
+
await onRefetchNeeded();
|
|
815
|
+
return;
|
|
816
|
+
} catch (refetchError) {
|
|
817
|
+
console.error("Failed to refetch file:", refetchError);
|
|
818
|
+
}
|
|
617
819
|
}
|
|
618
|
-
throw
|
|
619
|
-
}
|
|
620
|
-
const blob = await response.blob();
|
|
621
|
-
objectUrl = URL.createObjectURL(blob);
|
|
622
|
-
imageCache.set(cacheKey, objectUrl);
|
|
623
|
-
if (isMounted) {
|
|
624
|
-
setImageSrc(objectUrl);
|
|
625
|
-
setIsLoading(false);
|
|
626
|
-
refetchAttemptedRef.current = null;
|
|
820
|
+
throw fetchError;
|
|
627
821
|
}
|
|
628
822
|
} else {
|
|
629
823
|
throw new Error("No download URL available for file");
|
|
@@ -661,20 +855,25 @@ function FileImage({
|
|
|
661
855
|
...style
|
|
662
856
|
};
|
|
663
857
|
if (error) {
|
|
664
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.
|
|
858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
665
859
|
"div",
|
|
666
860
|
{
|
|
667
861
|
className,
|
|
668
862
|
style: {
|
|
669
863
|
...imageStyle,
|
|
670
864
|
display: "flex",
|
|
865
|
+
flexDirection: "column",
|
|
671
866
|
alignItems: "center",
|
|
672
867
|
justifyContent: "center",
|
|
673
|
-
backgroundColor: "
|
|
674
|
-
color: "
|
|
675
|
-
fontSize: "14px"
|
|
868
|
+
backgroundColor: "var(--plugable-base-surface)",
|
|
869
|
+
color: "var(--plugable-text-muted)",
|
|
870
|
+
fontSize: "14px",
|
|
871
|
+
gap: "8px"
|
|
676
872
|
},
|
|
677
|
-
children:
|
|
873
|
+
children: [
|
|
874
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { style: { width: "32px", height: "32px" }, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }),
|
|
875
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Failed to load" })
|
|
876
|
+
]
|
|
678
877
|
}
|
|
679
878
|
);
|
|
680
879
|
}
|
|
@@ -685,29 +884,29 @@ function FileImage({
|
|
|
685
884
|
className,
|
|
686
885
|
style: {
|
|
687
886
|
...imageStyle,
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
backgroundColor: "#f0f0f0"
|
|
887
|
+
position: "relative",
|
|
888
|
+
overflow: "hidden",
|
|
889
|
+
backgroundColor: "var(--plugable-base-surface)"
|
|
692
890
|
},
|
|
693
891
|
children: [
|
|
694
892
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
695
893
|
"div",
|
|
696
894
|
{
|
|
697
895
|
style: {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
896
|
+
position: "absolute",
|
|
897
|
+
top: 0,
|
|
898
|
+
left: "-100%",
|
|
899
|
+
width: "100%",
|
|
900
|
+
height: "100%",
|
|
901
|
+
background: "linear-gradient(90deg, transparent, var(--plugable-overlay), transparent)",
|
|
902
|
+
animation: "shimmer 1.5s infinite"
|
|
704
903
|
}
|
|
705
904
|
}
|
|
706
905
|
),
|
|
707
906
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
|
|
708
|
-
@keyframes
|
|
709
|
-
0% {
|
|
710
|
-
100% {
|
|
907
|
+
@keyframes shimmer {
|
|
908
|
+
0% { left: -100%; }
|
|
909
|
+
100% { left: 100%; }
|
|
711
910
|
}
|
|
712
911
|
` })
|
|
713
912
|
]
|
|
@@ -720,7 +919,11 @@ function FileImage({
|
|
|
720
919
|
src: imageSrc,
|
|
721
920
|
alt: alt || file.name,
|
|
722
921
|
className,
|
|
723
|
-
style:
|
|
922
|
+
style: {
|
|
923
|
+
...imageStyle,
|
|
924
|
+
opacity: isLoading ? 0 : 1,
|
|
925
|
+
transition: "opacity 0.3s ease-in"
|
|
926
|
+
},
|
|
724
927
|
onLoad: handleLoad,
|
|
725
928
|
onError: handleError
|
|
726
929
|
}
|
|
@@ -734,10 +937,33 @@ function clearImageCache() {
|
|
|
734
937
|
// src/components/FilePreview.tsx
|
|
735
938
|
var import_react5 = require("react");
|
|
736
939
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
940
|
+
var fileTypeColors = {
|
|
941
|
+
pdf: { bg: "transparent", text: "#94a3b8" },
|
|
942
|
+
// Slate-400
|
|
943
|
+
doc: { bg: "transparent", text: "#94a3b8" },
|
|
944
|
+
docx: { bg: "transparent", text: "#94a3b8" },
|
|
945
|
+
xls: { bg: "transparent", text: "#94a3b8" },
|
|
946
|
+
xlsx: { bg: "transparent", text: "#94a3b8" },
|
|
947
|
+
csv: { bg: "transparent", text: "#94a3b8" },
|
|
948
|
+
txt: { bg: "transparent", text: "#94a3b8" },
|
|
949
|
+
zip: { bg: "transparent", text: "#94a3b8" },
|
|
950
|
+
rar: { bg: "transparent", text: "#94a3b8" },
|
|
951
|
+
mp4: { bg: "transparent", text: "#94a3b8" },
|
|
952
|
+
mov: { bg: "transparent", text: "#94a3b8" },
|
|
953
|
+
avi: { bg: "transparent", text: "#94a3b8" },
|
|
954
|
+
mp3: { bg: "transparent", text: "#94a3b8" },
|
|
955
|
+
wav: { bg: "transparent", text: "#94a3b8" },
|
|
956
|
+
default: { bg: "transparent", text: "#64748b" }
|
|
957
|
+
// Slate-500
|
|
958
|
+
};
|
|
959
|
+
function getFileTypeColor(filename) {
|
|
960
|
+
const ext = filename.split(".").pop()?.toLowerCase() || "";
|
|
961
|
+
return fileTypeColors[ext] || fileTypeColors.default;
|
|
962
|
+
}
|
|
737
963
|
function FilePreview({
|
|
738
964
|
file: initialFile,
|
|
739
|
-
width =
|
|
740
|
-
height =
|
|
965
|
+
width = 120,
|
|
966
|
+
height = 120,
|
|
741
967
|
className,
|
|
742
968
|
style,
|
|
743
969
|
objectFit = "cover",
|
|
@@ -747,6 +973,7 @@ function FilePreview({
|
|
|
747
973
|
const { client } = usePlugable();
|
|
748
974
|
const [file, setFile] = (0, import_react5.useState)(initialFile);
|
|
749
975
|
const [isRefetching, setIsRefetching] = (0, import_react5.useState)(false);
|
|
976
|
+
const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
|
|
750
977
|
(0, import_react5.useEffect)(() => {
|
|
751
978
|
setFile(initialFile);
|
|
752
979
|
}, [initialFile.id, initialFile.download_url]);
|
|
@@ -761,45 +988,105 @@ function FilePreview({
|
|
|
761
988
|
} finally {
|
|
762
989
|
setIsRefetching(false);
|
|
763
990
|
}
|
|
764
|
-
}, [file.id, client]);
|
|
991
|
+
}, [file.id, client, isRefetching]);
|
|
765
992
|
const isImage = file.content_type.startsWith("image/");
|
|
766
993
|
const containerStyle = {
|
|
767
994
|
width,
|
|
768
995
|
height,
|
|
769
|
-
borderRadius:
|
|
996
|
+
borderRadius: "8px",
|
|
770
997
|
overflow: "hidden",
|
|
771
|
-
|
|
998
|
+
position: "relative",
|
|
772
999
|
display: "flex",
|
|
773
1000
|
alignItems: "center",
|
|
774
1001
|
justifyContent: "center",
|
|
775
|
-
border: "1px solid
|
|
1002
|
+
border: isHovered ? "1px solid var(--plugable-accent-primary)" : "1px solid var(--plugable-base-border)",
|
|
1003
|
+
background: "var(--plugable-base-surface)",
|
|
1004
|
+
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
1005
|
+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)",
|
|
1006
|
+
cursor: "pointer",
|
|
776
1007
|
...style
|
|
777
1008
|
};
|
|
778
1009
|
if (isImage) {
|
|
779
1010
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
780
|
-
|
|
1011
|
+
"div",
|
|
781
1012
|
{
|
|
782
|
-
|
|
783
|
-
width,
|
|
784
|
-
height,
|
|
785
|
-
objectFit,
|
|
1013
|
+
style: containerStyle,
|
|
786
1014
|
className,
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
1015
|
+
onMouseEnter: () => setIsHovered(true),
|
|
1016
|
+
onMouseLeave: () => setIsHovered(false),
|
|
1017
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1018
|
+
FileImage,
|
|
1019
|
+
{
|
|
1020
|
+
file,
|
|
1021
|
+
width,
|
|
1022
|
+
height,
|
|
1023
|
+
objectFit,
|
|
1024
|
+
style: { borderRadius: "8px" },
|
|
1025
|
+
onRefetchNeeded: handleRefetch
|
|
1026
|
+
}
|
|
1027
|
+
)
|
|
790
1028
|
}
|
|
791
1029
|
);
|
|
792
1030
|
}
|
|
793
1031
|
if (renderNonImage) {
|
|
794
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1032
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1033
|
+
"div",
|
|
1034
|
+
{
|
|
1035
|
+
className,
|
|
1036
|
+
style: containerStyle,
|
|
1037
|
+
onMouseEnter: () => setIsHovered(true),
|
|
1038
|
+
onMouseLeave: () => setIsHovered(false),
|
|
1039
|
+
children: renderNonImage(file)
|
|
1040
|
+
}
|
|
1041
|
+
);
|
|
795
1042
|
}
|
|
796
1043
|
const extension = file.name.split(".").pop()?.toUpperCase() || "FILE";
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
1044
|
+
const colors = getFileTypeColor(file.name);
|
|
1045
|
+
const badgeStyle = {
|
|
1046
|
+
padding: "2px 8px",
|
|
1047
|
+
borderRadius: "4px",
|
|
1048
|
+
border: "1px solid var(--plugable-base-border)",
|
|
1049
|
+
backgroundColor: "var(--plugable-overlay)",
|
|
1050
|
+
display: "inline-flex",
|
|
1051
|
+
alignItems: "center",
|
|
1052
|
+
justifyContent: "center"
|
|
1053
|
+
};
|
|
1054
|
+
const extensionStyle = {
|
|
1055
|
+
fontSize: "11px",
|
|
1056
|
+
fontWeight: 600,
|
|
1057
|
+
color: "var(--plugable-text-secondary)",
|
|
1058
|
+
textTransform: "uppercase",
|
|
1059
|
+
letterSpacing: "0.05em"
|
|
1060
|
+
};
|
|
1061
|
+
const iconStyle = {
|
|
1062
|
+
width: "28px",
|
|
1063
|
+
height: "28px",
|
|
1064
|
+
marginBottom: "6px",
|
|
1065
|
+
color: colors.text
|
|
1066
|
+
};
|
|
1067
|
+
const wrapperStyle = {
|
|
1068
|
+
textAlign: "center",
|
|
1069
|
+
padding: "12px",
|
|
1070
|
+
display: "flex",
|
|
1071
|
+
flexDirection: "column",
|
|
1072
|
+
alignItems: "center",
|
|
1073
|
+
justifyContent: "center",
|
|
1074
|
+
width: "100%",
|
|
1075
|
+
height: "100%"
|
|
1076
|
+
};
|
|
1077
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1078
|
+
"div",
|
|
1079
|
+
{
|
|
1080
|
+
className,
|
|
1081
|
+
style: containerStyle,
|
|
1082
|
+
onMouseEnter: () => setIsHovered(true),
|
|
1083
|
+
onMouseLeave: () => setIsHovered(false),
|
|
1084
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: wrapperStyle, children: [
|
|
1085
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { style: iconStyle, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" }) }),
|
|
1086
|
+
showExtension && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: badgeStyle, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: extensionStyle, children: extension }) })
|
|
1087
|
+
] })
|
|
1088
|
+
}
|
|
1089
|
+
);
|
|
803
1090
|
}
|
|
804
1091
|
// Annotate the CommonJS export names for ESM import in node:
|
|
805
1092
|
0 && (module.exports = {
|
|
@@ -808,7 +1095,10 @@ function FilePreview({
|
|
|
808
1095
|
FileList,
|
|
809
1096
|
FilePreview,
|
|
810
1097
|
PlugableProvider,
|
|
1098
|
+
applyCSSVariables,
|
|
811
1099
|
clearImageCache,
|
|
1100
|
+
generateCSSVariables,
|
|
1101
|
+
getThemeColors,
|
|
812
1102
|
useFiles,
|
|
813
1103
|
usePlugable
|
|
814
1104
|
});
|