@plugable-io/react 0.0.7 → 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/dist/index.mjs CHANGED
@@ -1,6 +1,112 @@
1
1
  // src/PlugableProvider.tsx
2
- import { createContext, useContext, useMemo, useCallback, useRef, useState } from "react";
2
+ import { createContext, useContext, useMemo, useCallback, useRef, useState, useEffect } from "react";
3
3
  import { BucketClient } from "@plugable-io/js";
4
+
5
+ // src/utils/theme.ts
6
+ var defaultLightTheme = {
7
+ // Purple to blue accent gradient
8
+ accentPrimary: "#9333ea",
9
+ // purple-600
10
+ accentSecondary: "#2563eb",
11
+ // blue-600
12
+ accentHover: "#7c3aed",
13
+ // purple-700
14
+ // Light backgrounds
15
+ baseBg: "#ffffff",
16
+ baseSurface: "#f8fafc",
17
+ // slate-50
18
+ baseBorder: "#e2e8f0",
19
+ // slate-200
20
+ // Dark text on light
21
+ textPrimary: "#0f172a",
22
+ // slate-900
23
+ textSecondary: "#475569",
24
+ // slate-600
25
+ textMuted: "#94a3b8",
26
+ // slate-400
27
+ // State colors
28
+ success: "#10b981",
29
+ // green-500
30
+ error: "#ef4444",
31
+ // red-500
32
+ warning: "#f59e0b",
33
+ // amber-500
34
+ // Overlay
35
+ overlay: "rgba(0, 0, 0, 0.05)",
36
+ backdropBlur: "blur(12px)"
37
+ };
38
+ var defaultDarkTheme = {
39
+ // Purple to blue accent gradient (same as light)
40
+ accentPrimary: "#9333ea",
41
+ accentSecondary: "#2563eb",
42
+ accentHover: "#a855f7",
43
+ // purple-500 (lighter for dark mode)
44
+ // Dark backgrounds
45
+ baseBg: "#0f172a",
46
+ // slate-900
47
+ baseSurface: "rgba(30, 41, 59, 0.5)",
48
+ // slate-800/50 with transparency
49
+ baseBorder: "rgba(255, 255, 255, 0.1)",
50
+ // Light text on dark
51
+ textPrimary: "#f1f5f9",
52
+ // slate-100
53
+ textSecondary: "#cbd5e1",
54
+ // slate-300
55
+ textMuted: "#64748b",
56
+ // slate-500
57
+ // State colors (slightly adjusted for dark mode)
58
+ success: "#34d399",
59
+ // green-400
60
+ error: "#f87171",
61
+ // red-400
62
+ warning: "#fbbf24",
63
+ // amber-400
64
+ // Overlay
65
+ overlay: "rgba(0, 0, 0, 0.3)",
66
+ backdropBlur: "blur(24px)"
67
+ };
68
+ function getThemeColors(config = {}) {
69
+ const { theme = "dark", accentColor, baseColor } = config;
70
+ const isDark = theme === "dark" || theme === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches;
71
+ const colors = isDark ? { ...defaultDarkTheme } : { ...defaultLightTheme };
72
+ if (accentColor) {
73
+ colors.accentPrimary = accentColor;
74
+ colors.accentSecondary = accentColor;
75
+ colors.accentHover = accentColor;
76
+ }
77
+ if (baseColor) {
78
+ colors.baseBg = baseColor;
79
+ colors.baseSurface = baseColor;
80
+ }
81
+ return colors;
82
+ }
83
+ function generateCSSVariables(colors) {
84
+ return {
85
+ "--plugable-accent-primary": colors.accentPrimary,
86
+ "--plugable-accent-secondary": colors.accentSecondary,
87
+ "--plugable-accent-hover": colors.accentHover,
88
+ "--plugable-base-bg": colors.baseBg,
89
+ "--plugable-base-surface": colors.baseSurface,
90
+ "--plugable-base-border": colors.baseBorder,
91
+ "--plugable-text-primary": colors.textPrimary,
92
+ "--plugable-text-secondary": colors.textSecondary,
93
+ "--plugable-text-muted": colors.textMuted,
94
+ "--plugable-success": colors.success,
95
+ "--plugable-error": colors.error,
96
+ "--plugable-warning": colors.warning,
97
+ "--plugable-overlay": colors.overlay,
98
+ "--plugable-backdrop-blur": colors.backdropBlur
99
+ };
100
+ }
101
+ function applyCSSVariables(element, config = {}) {
102
+ const colors = getThemeColors(config);
103
+ const variables = generateCSSVariables(colors);
104
+ Object.entries(variables).forEach(([key, value]) => {
105
+ element.style.setProperty(key, value);
106
+ });
107
+ }
108
+
109
+ // src/PlugableProvider.tsx
4
110
  import { jsx } from "react/jsx-runtime";
5
111
  var PlugableContext = createContext(null);
6
112
  function createAuthTokenGetter(authProvider, clerkJWTTemplate) {
@@ -45,6 +151,13 @@ function createAuthTokenGetter(authProvider, clerkJWTTemplate) {
45
151
  "Firebase not found. Please ensure firebase is installed and initialized, or provide a custom getToken function."
46
152
  );
47
153
  };
154
+ case "generic_jwks":
155
+ case "generic_jwt":
156
+ return async () => {
157
+ throw new Error(
158
+ `Manual token required for ${authProvider}. Please provide a custom getToken function.`
159
+ );
160
+ };
48
161
  default:
49
162
  throw new Error(
50
163
  `Unknown auth provider: ${authProvider}. Please provide either a valid authProvider or a custom getToken function.`
@@ -58,11 +171,15 @@ function PlugableProvider({
58
171
  authProvider,
59
172
  clerkJWTTemplate,
60
173
  baseUrl,
61
- staleTime = 5 * 60 * 1e3
174
+ staleTime = 5 * 60 * 1e3,
62
175
  // Default 5 minutes
176
+ accentColor,
177
+ baseColor,
178
+ theme = "dark"
63
179
  }) {
64
180
  const listenersRef = useRef({});
65
181
  const [cache, setCacheState] = useState(/* @__PURE__ */ new Map());
182
+ const containerRef = useRef(null);
66
183
  const client = useMemo(() => {
67
184
  if (!getToken && !authProvider) {
68
185
  throw new Error(
@@ -132,7 +249,12 @@ function PlugableProvider({
132
249
  }),
133
250
  [client, bucketId, on, emit, baseUrl, staleTime, getCache, setCache, invalidateCache]
134
251
  );
135
- return /* @__PURE__ */ jsx(PlugableContext.Provider, { value, children });
252
+ useEffect(() => {
253
+ if (containerRef.current) {
254
+ applyCSSVariables(containerRef.current, { accentColor, baseColor, theme });
255
+ }
256
+ }, [accentColor, baseColor, theme]);
257
+ return /* @__PURE__ */ jsx(PlugableContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "plugable-root", children }) });
136
258
  }
137
259
  function usePlugable() {
138
260
  const context = useContext(PlugableContext);
@@ -277,6 +399,74 @@ function Dropzone({
277
399
  }
278
400
  );
279
401
  }
402
+ const containerStyle = {
403
+ position: "relative",
404
+ border: `2px dashed ${isDragActive ? "var(--plugable-accent-primary)" : "var(--plugable-base-border)"}`,
405
+ borderRadius: "12px",
406
+ padding: "48px 24px",
407
+ textAlign: "center",
408
+ cursor: "pointer",
409
+ background: isDragActive ? "var(--plugable-accent-primary)10" : "var(--plugable-base-surface)",
410
+ backdropFilter: "var(--plugable-backdrop-blur)",
411
+ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
412
+ ...style
413
+ };
414
+ const iconContainerStyle = {
415
+ width: "48px",
416
+ height: "48px",
417
+ margin: "0 auto 16px",
418
+ borderRadius: "50%",
419
+ display: "flex",
420
+ alignItems: "center",
421
+ justifyContent: "center",
422
+ background: isDragActive ? `linear-gradient(135deg, var(--plugable-accent-primary), var(--plugable-accent-secondary))` : "var(--plugable-overlay)",
423
+ transition: "all 0.3s ease",
424
+ transform: isDragActive ? "scale(1.1)" : "scale(1)"
425
+ };
426
+ const cloudIconStyle = {
427
+ width: "24px",
428
+ height: "24px",
429
+ color: isDragActive ? "#fff" : "var(--plugable-accent-primary)"
430
+ };
431
+ const titleStyle = {
432
+ margin: 0,
433
+ fontWeight: 600,
434
+ fontSize: "16px",
435
+ color: "var(--plugable-text-primary)",
436
+ marginBottom: "8px"
437
+ };
438
+ const subtitleStyle = {
439
+ margin: 0,
440
+ fontSize: "14px",
441
+ color: "var(--plugable-text-secondary)"
442
+ };
443
+ const maxFilesStyle = {
444
+ margin: "4px 0 0 0",
445
+ fontSize: "12px",
446
+ color: "var(--plugable-text-muted)"
447
+ };
448
+ const progressContainerStyle = {
449
+ marginTop: "16px"
450
+ };
451
+ const progressItemStyle = {
452
+ marginBottom: "12px",
453
+ textAlign: "left"
454
+ };
455
+ const progressLabelStyle = {
456
+ fontSize: "14px",
457
+ color: "var(--plugable-text-secondary)",
458
+ marginBottom: "6px",
459
+ display: "flex",
460
+ justifyContent: "space-between",
461
+ alignItems: "center"
462
+ };
463
+ const progressBarBgStyle = {
464
+ width: "100%",
465
+ height: "6px",
466
+ backgroundColor: "var(--plugable-overlay)",
467
+ borderRadius: "3px",
468
+ overflow: "hidden"
469
+ };
280
470
  return /* @__PURE__ */ jsxs(
281
471
  "div",
282
472
  {
@@ -285,16 +475,7 @@ function Dropzone({
285
475
  onDragLeave: handleDragLeave,
286
476
  onClick: openFileDialog,
287
477
  className,
288
- style: {
289
- border: `2px dashed ${isDragActive ? "#0070f3" : "#ccc"}`,
290
- borderRadius: "8px",
291
- padding: "40px 20px",
292
- textAlign: "center",
293
- cursor: "pointer",
294
- backgroundColor: isDragActive ? "#f0f8ff" : "#fafafa",
295
- transition: "all 0.2s ease",
296
- ...style
297
- },
478
+ style: containerStyle,
298
479
  children: [
299
480
  /* @__PURE__ */ jsx2(
300
481
  "input",
@@ -308,42 +489,34 @@ function Dropzone({
308
489
  }
309
490
  ),
310
491
  isUploading ? /* @__PURE__ */ jsxs("div", { children: [
311
- /* @__PURE__ */ jsx2("p", { style: { margin: 0, fontWeight: "bold", color: "#333" }, children: "Uploading..." }),
312
- /* @__PURE__ */ jsx2("div", { style: { marginTop: "16px" }, children: Object.entries(uploadProgress).map(([fileName, progress]) => /* @__PURE__ */ jsxs("div", { style: { marginBottom: "8px" }, children: [
313
- /* @__PURE__ */ jsxs("div", { style: { fontSize: "14px", color: "#666", marginBottom: "4px" }, children: [
314
- fileName,
315
- ": ",
316
- progress,
317
- "%"
492
+ /* @__PURE__ */ jsx2("div", { style: iconContainerStyle, children: /* @__PURE__ */ jsx2("svg", { style: cloudIconStyle, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx2("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" }) }) }),
493
+ /* @__PURE__ */ jsx2("p", { style: titleStyle, children: "Uploading..." }),
494
+ /* @__PURE__ */ jsx2("div", { style: progressContainerStyle, children: Object.entries(uploadProgress).map(([fileName, progress]) => /* @__PURE__ */ jsxs("div", { style: progressItemStyle, children: [
495
+ /* @__PURE__ */ jsxs("div", { style: progressLabelStyle, children: [
496
+ /* @__PURE__ */ jsx2("span", { children: fileName }),
497
+ /* @__PURE__ */ jsxs("span", { style: { fontWeight: 600 }, children: [
498
+ progress,
499
+ "%"
500
+ ] })
318
501
  ] }),
319
- /* @__PURE__ */ jsx2(
502
+ /* @__PURE__ */ jsx2("div", { style: progressBarBgStyle, children: /* @__PURE__ */ jsx2(
320
503
  "div",
321
504
  {
322
505
  style: {
323
- width: "100%",
324
- height: "8px",
325
- backgroundColor: "#e0e0e0",
326
- borderRadius: "4px",
327
- overflow: "hidden"
328
- },
329
- children: /* @__PURE__ */ jsx2(
330
- "div",
331
- {
332
- style: {
333
- width: `${progress}%`,
334
- height: "100%",
335
- backgroundColor: "#0070f3",
336
- transition: "width 0.3s ease"
337
- }
338
- }
339
- )
506
+ width: `${progress}%`,
507
+ height: "100%",
508
+ background: `linear-gradient(90deg, var(--plugable-accent-primary), var(--plugable-accent-secondary))`,
509
+ transition: "width 0.3s ease",
510
+ borderRadius: "3px"
511
+ }
340
512
  }
341
- )
513
+ ) })
342
514
  ] }, fileName)) })
343
515
  ] }) : /* @__PURE__ */ jsxs("div", { children: [
344
- /* @__PURE__ */ jsx2("p", { style: { margin: 0, fontWeight: "bold", fontSize: "16px", color: "#333" }, children: isDragActive ? "Drop files here" : "Drag and drop files here" }),
345
- /* @__PURE__ */ jsx2("p", { style: { margin: "8px 0 0 0", fontSize: "14px", color: "#666" }, children: "or click to select files" }),
346
- maxFiles && maxFiles > 1 && /* @__PURE__ */ jsxs("p", { style: { margin: "4px 0 0 0", fontSize: "12px", color: "#999" }, children: [
516
+ /* @__PURE__ */ jsx2("div", { style: iconContainerStyle, children: /* @__PURE__ */ jsx2("svg", { style: cloudIconStyle, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx2("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" }) }) }),
517
+ /* @__PURE__ */ jsx2("p", { style: titleStyle, children: isDragActive ? "Drop files here" : "Click to upload or drag and drop" }),
518
+ /* @__PURE__ */ jsx2("p", { style: subtitleStyle, children: accept ? `Accepted: ${accept}` : "Any file type" }),
519
+ maxFiles && maxFiles > 1 && /* @__PURE__ */ jsxs("p", { style: maxFilesStyle, children: [
347
520
  "(Maximum ",
348
521
  maxFiles,
349
522
  " files)"
@@ -355,7 +528,7 @@ function Dropzone({
355
528
  }
356
529
 
357
530
  // src/hooks/useFiles.ts
358
- import { useState as useState3, useCallback as useCallback3, useEffect, useMemo as useMemo2, useRef as useRef2 } from "react";
531
+ import { useState as useState3, useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2 } from "react";
359
532
  function useFiles({
360
533
  metadata,
361
534
  startPage = 1,
@@ -440,7 +613,7 @@ function useFiles({
440
613
  setIsLoading(false);
441
614
  }
442
615
  }, [client, stableMetadata, mediaType, perPage, orderBy, orderDirection, getCache, setCache, effectiveStaleTime]);
443
- useEffect(() => {
616
+ useEffect2(() => {
444
617
  const paramsChanged = previousParamsRef.current !== null && previousParamsRef.current !== paramsKeyWithPage;
445
618
  const isInitialMount = isInitialMountRef.current;
446
619
  previousParamsRef.current = paramsKeyWithPage;
@@ -461,7 +634,7 @@ function useFiles({
461
634
  fetchFiles(page, true);
462
635
  }
463
636
  }, [paramsKeyWithPage, autoLoad, getCache, effectiveStaleTime, fetchFiles, page]);
464
- useEffect(() => {
637
+ useEffect2(() => {
465
638
  const unsubscribe = on("file.uploaded", () => {
466
639
  fetchFiles(page, true);
467
640
  });
@@ -523,7 +696,7 @@ function FileList({
523
696
  }
524
697
 
525
698
  // src/components/FileImage.tsx
526
- import { useEffect as useEffect2, useState as useState4, useRef as useRef3 } from "react";
699
+ import { useEffect as useEffect3, useState as useState4, useRef as useRef3 } from "react";
527
700
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
528
701
  var imageCache = /* @__PURE__ */ new Map();
529
702
  function FileImage({
@@ -543,7 +716,7 @@ function FileImage({
543
716
  const [isLoading, setIsLoading] = useState4(true);
544
717
  const [error, setError] = useState4(null);
545
718
  const refetchAttemptedRef = useRef3(null);
546
- useEffect2(() => {
719
+ useEffect3(() => {
547
720
  let isMounted = true;
548
721
  let objectUrl = null;
549
722
  const loadImage = async () => {
@@ -636,20 +809,25 @@ function FileImage({
636
809
  ...style
637
810
  };
638
811
  if (error) {
639
- return /* @__PURE__ */ jsx4(
812
+ return /* @__PURE__ */ jsxs2(
640
813
  "div",
641
814
  {
642
815
  className,
643
816
  style: {
644
817
  ...imageStyle,
645
818
  display: "flex",
819
+ flexDirection: "column",
646
820
  alignItems: "center",
647
821
  justifyContent: "center",
648
- backgroundColor: "#f0f0f0",
649
- color: "#999",
650
- fontSize: "14px"
822
+ backgroundColor: "var(--plugable-base-surface)",
823
+ color: "var(--plugable-text-muted)",
824
+ fontSize: "14px",
825
+ gap: "8px"
651
826
  },
652
- children: "Failed to load image"
827
+ children: [
828
+ /* @__PURE__ */ jsx4("svg", { style: { width: "32px", height: "32px" }, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx4("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" }) }),
829
+ /* @__PURE__ */ jsx4("span", { children: "Failed to load" })
830
+ ]
653
831
  }
654
832
  );
655
833
  }
@@ -660,29 +838,29 @@ function FileImage({
660
838
  className,
661
839
  style: {
662
840
  ...imageStyle,
663
- display: "flex",
664
- alignItems: "center",
665
- justifyContent: "center",
666
- backgroundColor: "#f0f0f0"
841
+ position: "relative",
842
+ overflow: "hidden",
843
+ backgroundColor: "var(--plugable-base-surface)"
667
844
  },
668
845
  children: [
669
846
  /* @__PURE__ */ jsx4(
670
847
  "div",
671
848
  {
672
849
  style: {
673
- width: "40px",
674
- height: "40px",
675
- border: "3px solid #e0e0e0",
676
- borderTop: "3px solid #0070f3",
677
- borderRadius: "50%",
678
- animation: "spin 1s linear infinite"
850
+ position: "absolute",
851
+ top: 0,
852
+ left: "-100%",
853
+ width: "100%",
854
+ height: "100%",
855
+ background: "linear-gradient(90deg, transparent, var(--plugable-overlay), transparent)",
856
+ animation: "shimmer 1.5s infinite"
679
857
  }
680
858
  }
681
859
  ),
682
860
  /* @__PURE__ */ jsx4("style", { children: `
683
- @keyframes spin {
684
- 0% { transform: rotate(0deg); }
685
- 100% { transform: rotate(360deg); }
861
+ @keyframes shimmer {
862
+ 0% { left: -100%; }
863
+ 100% { left: 100%; }
686
864
  }
687
865
  ` })
688
866
  ]
@@ -695,7 +873,11 @@ function FileImage({
695
873
  src: imageSrc,
696
874
  alt: alt || file.name,
697
875
  className,
698
- style: imageStyle,
876
+ style: {
877
+ ...imageStyle,
878
+ opacity: isLoading ? 0 : 1,
879
+ transition: "opacity 0.3s ease-in"
880
+ },
699
881
  onLoad: handleLoad,
700
882
  onError: handleError
701
883
  }
@@ -707,12 +889,35 @@ function clearImageCache() {
707
889
  }
708
890
 
709
891
  // src/components/FilePreview.tsx
710
- import { useState as useState5, useCallback as useCallback4, useEffect as useEffect3 } from "react";
711
- import { jsx as jsx5 } from "react/jsx-runtime";
892
+ import { useState as useState5, useCallback as useCallback4, useEffect as useEffect4 } from "react";
893
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
894
+ var fileTypeColors = {
895
+ pdf: { bg: "transparent", text: "#94a3b8" },
896
+ // Slate-400
897
+ doc: { bg: "transparent", text: "#94a3b8" },
898
+ docx: { bg: "transparent", text: "#94a3b8" },
899
+ xls: { bg: "transparent", text: "#94a3b8" },
900
+ xlsx: { bg: "transparent", text: "#94a3b8" },
901
+ csv: { bg: "transparent", text: "#94a3b8" },
902
+ txt: { bg: "transparent", text: "#94a3b8" },
903
+ zip: { bg: "transparent", text: "#94a3b8" },
904
+ rar: { bg: "transparent", text: "#94a3b8" },
905
+ mp4: { bg: "transparent", text: "#94a3b8" },
906
+ mov: { bg: "transparent", text: "#94a3b8" },
907
+ avi: { bg: "transparent", text: "#94a3b8" },
908
+ mp3: { bg: "transparent", text: "#94a3b8" },
909
+ wav: { bg: "transparent", text: "#94a3b8" },
910
+ default: { bg: "transparent", text: "#64748b" }
911
+ // Slate-500
912
+ };
913
+ function getFileTypeColor(filename) {
914
+ const ext = filename.split(".").pop()?.toLowerCase() || "";
915
+ return fileTypeColors[ext] || fileTypeColors.default;
916
+ }
712
917
  function FilePreview({
713
918
  file: initialFile,
714
- width = 80,
715
- height = 80,
919
+ width = 120,
920
+ height = 120,
716
921
  className,
717
922
  style,
718
923
  objectFit = "cover",
@@ -722,7 +927,8 @@ function FilePreview({
722
927
  const { client } = usePlugable();
723
928
  const [file, setFile] = useState5(initialFile);
724
929
  const [isRefetching, setIsRefetching] = useState5(false);
725
- useEffect3(() => {
930
+ const [isHovered, setIsHovered] = useState5(false);
931
+ useEffect4(() => {
726
932
  setFile(initialFile);
727
933
  }, [initialFile.id, initialFile.download_url]);
728
934
  const handleRefetch = useCallback4(async () => {
@@ -736,45 +942,105 @@ function FilePreview({
736
942
  } finally {
737
943
  setIsRefetching(false);
738
944
  }
739
- }, [file.id, client]);
945
+ }, [file.id, client, isRefetching]);
740
946
  const isImage = file.content_type.startsWith("image/");
741
947
  const containerStyle = {
742
948
  width,
743
949
  height,
744
- borderRadius: 4,
950
+ borderRadius: "8px",
745
951
  overflow: "hidden",
746
- backgroundColor: "#f5f5f5",
952
+ position: "relative",
747
953
  display: "flex",
748
954
  alignItems: "center",
749
955
  justifyContent: "center",
750
- border: "1px solid #eee",
956
+ border: isHovered ? "1px solid var(--plugable-accent-primary)" : "1px solid var(--plugable-base-border)",
957
+ background: "var(--plugable-base-surface)",
958
+ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
959
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)",
960
+ cursor: "pointer",
751
961
  ...style
752
962
  };
753
963
  if (isImage) {
754
964
  return /* @__PURE__ */ jsx5(
755
- FileImage,
965
+ "div",
756
966
  {
757
- file,
758
- width,
759
- height,
760
- objectFit,
967
+ style: containerStyle,
761
968
  className,
762
- style,
763
- borderRadius: 4,
764
- onRefetchNeeded: handleRefetch
969
+ onMouseEnter: () => setIsHovered(true),
970
+ onMouseLeave: () => setIsHovered(false),
971
+ children: /* @__PURE__ */ jsx5(
972
+ FileImage,
973
+ {
974
+ file,
975
+ width,
976
+ height,
977
+ objectFit,
978
+ style: { borderRadius: "8px" },
979
+ onRefetchNeeded: handleRefetch
980
+ }
981
+ )
765
982
  }
766
983
  );
767
984
  }
768
985
  if (renderNonImage) {
769
- return /* @__PURE__ */ jsx5("div", { className, style: containerStyle, children: renderNonImage(file) });
986
+ return /* @__PURE__ */ jsx5(
987
+ "div",
988
+ {
989
+ className,
990
+ style: containerStyle,
991
+ onMouseEnter: () => setIsHovered(true),
992
+ onMouseLeave: () => setIsHovered(false),
993
+ children: renderNonImage(file)
994
+ }
995
+ );
770
996
  }
771
997
  const extension = file.name.split(".").pop()?.toUpperCase() || "FILE";
772
- return /* @__PURE__ */ jsx5("div", { className, style: containerStyle, children: showExtension && /* @__PURE__ */ jsx5("span", { style: {
773
- fontSize: "12px",
774
- fontWeight: "bold",
775
- color: "#666",
776
- textTransform: "uppercase"
777
- }, children: extension }) });
998
+ const colors = getFileTypeColor(file.name);
999
+ const badgeStyle = {
1000
+ padding: "2px 8px",
1001
+ borderRadius: "4px",
1002
+ border: "1px solid var(--plugable-base-border)",
1003
+ backgroundColor: "var(--plugable-overlay)",
1004
+ display: "inline-flex",
1005
+ alignItems: "center",
1006
+ justifyContent: "center"
1007
+ };
1008
+ const extensionStyle = {
1009
+ fontSize: "11px",
1010
+ fontWeight: 600,
1011
+ color: "var(--plugable-text-secondary)",
1012
+ textTransform: "uppercase",
1013
+ letterSpacing: "0.05em"
1014
+ };
1015
+ const iconStyle = {
1016
+ width: "28px",
1017
+ height: "28px",
1018
+ marginBottom: "6px",
1019
+ color: colors.text
1020
+ };
1021
+ const wrapperStyle = {
1022
+ textAlign: "center",
1023
+ padding: "12px",
1024
+ display: "flex",
1025
+ flexDirection: "column",
1026
+ alignItems: "center",
1027
+ justifyContent: "center",
1028
+ width: "100%",
1029
+ height: "100%"
1030
+ };
1031
+ return /* @__PURE__ */ jsx5(
1032
+ "div",
1033
+ {
1034
+ className,
1035
+ style: containerStyle,
1036
+ onMouseEnter: () => setIsHovered(true),
1037
+ onMouseLeave: () => setIsHovered(false),
1038
+ children: /* @__PURE__ */ jsxs3("div", { style: wrapperStyle, children: [
1039
+ /* @__PURE__ */ jsx5("svg", { style: iconStyle, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("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" }) }),
1040
+ showExtension && /* @__PURE__ */ jsx5("div", { style: badgeStyle, children: /* @__PURE__ */ jsx5("span", { style: extensionStyle, children: extension }) })
1041
+ ] })
1042
+ }
1043
+ );
778
1044
  }
779
1045
  export {
780
1046
  Dropzone,
@@ -782,7 +1048,10 @@ export {
782
1048
  FileList,
783
1049
  FilePreview,
784
1050
  PlugableProvider,
1051
+ applyCSSVariables,
785
1052
  clearImageCache,
1053
+ generateCSSVariables,
1054
+ getThemeColors,
786
1055
  useFiles,
787
1056
  usePlugable
788
1057
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plugable-io/react",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "React components and hooks for Plugable File Management API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",