@moontra/moonui-pro 2.11.0 → 2.11.1

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.d.ts CHANGED
@@ -1774,6 +1774,26 @@ interface FileUploadProProps {
1774
1774
  uploadStrategy?: 'direct' | 'presigned' | 'multipart';
1775
1775
  endpoint?: string;
1776
1776
  headers?: Record<string, string>;
1777
+ serviceConfig?: {
1778
+ type: 'aws-s3' | 'cloudinary' | 'firebase' | 'custom';
1779
+ awsConfig?: {
1780
+ region: string;
1781
+ bucket: string;
1782
+ accessKeyId?: string;
1783
+ secretAccessKey?: string;
1784
+ sessionToken?: string;
1785
+ };
1786
+ cloudinaryConfig?: {
1787
+ cloudName: string;
1788
+ uploadPreset: string;
1789
+ apiKey?: string;
1790
+ apiSecret?: string;
1791
+ };
1792
+ firebaseConfig?: {
1793
+ storageBucket: string;
1794
+ folder?: string;
1795
+ };
1796
+ };
1777
1797
  onUpload?: (files: FileUploadItem[]) => Promise<void>;
1778
1798
  onProgress?: (fileId: string, progress: number) => void;
1779
1799
  onComplete?: (fileId: string, result: any) => void;
package/dist/index.mjs CHANGED
@@ -58771,23 +58771,54 @@ var generateFileHash = async (file) => {
58771
58771
  };
58772
58772
  var createImagePreview = (file) => {
58773
58773
  return new Promise((resolve) => {
58774
- const img = new Image();
58775
- const url = URL.createObjectURL(file);
58776
- img.onload = () => {
58777
- resolve({
58778
- type: "image",
58779
- url,
58780
- thumbnail: url,
58781
- dimensions: { width: img.width, height: img.height }
58782
- });
58774
+ const reader = new FileReader();
58775
+ reader.onload = (e) => {
58776
+ const result = e.target?.result;
58777
+ const img = new Image();
58778
+ img.onload = () => {
58779
+ const canvas = document.createElement("canvas");
58780
+ const ctx = canvas.getContext("2d");
58781
+ const maxThumbSize = 400;
58782
+ let thumbWidth = img.width;
58783
+ let thumbHeight = img.height;
58784
+ if (thumbWidth > maxThumbSize || thumbHeight > maxThumbSize) {
58785
+ const aspectRatio = img.width / img.height;
58786
+ if (aspectRatio > 1) {
58787
+ thumbWidth = maxThumbSize;
58788
+ thumbHeight = maxThumbSize / aspectRatio;
58789
+ } else {
58790
+ thumbHeight = maxThumbSize;
58791
+ thumbWidth = maxThumbSize * aspectRatio;
58792
+ }
58793
+ }
58794
+ canvas.width = thumbWidth;
58795
+ canvas.height = thumbHeight;
58796
+ ctx.drawImage(img, 0, 0, thumbWidth, thumbHeight);
58797
+ const thumbnail = canvas.toDataURL("image/jpeg", 0.9);
58798
+ resolve({
58799
+ type: "image",
58800
+ url: result,
58801
+ thumbnail,
58802
+ dimensions: { width: img.width, height: img.height }
58803
+ });
58804
+ };
58805
+ img.onerror = () => {
58806
+ resolve({
58807
+ type: "image",
58808
+ url: result,
58809
+ thumbnail: result
58810
+ });
58811
+ };
58812
+ img.src = result;
58783
58813
  };
58784
- img.onerror = () => {
58814
+ reader.onerror = () => {
58815
+ const url = URL.createObjectURL(file);
58785
58816
  resolve({
58786
58817
  type: "image",
58787
58818
  url
58788
58819
  });
58789
58820
  };
58790
- img.src = url;
58821
+ reader.readAsDataURL(file);
58791
58822
  });
58792
58823
  };
58793
58824
  var createVideoPreview = (file) => {
@@ -58839,16 +58870,16 @@ var BulkActions = ({
58839
58870
  children: [
58840
58871
  /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium", children: [
58841
58872
  selectedIds.length,
58842
- " dosya se\xE7ildi"
58873
+ " files selected"
58843
58874
  ] }),
58844
58875
  /* @__PURE__ */ jsxs("div", { className: "flex gap-1 ml-auto", children: [
58845
58876
  /* @__PURE__ */ jsxs(MoonUIButtonPro, { variant: "ghost", size: "sm", onClick: onBulkDownload, children: [
58846
58877
  /* @__PURE__ */ jsx(Download, { className: "h-4 w-4 mr-1" }),
58847
- "\u0130ndir"
58878
+ "Download"
58848
58879
  ] }),
58849
58880
  /* @__PURE__ */ jsxs(MoonUIButtonPro, { variant: "ghost", size: "sm", onClick: onBulkRemove, children: [
58850
58881
  /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4 mr-1" }),
58851
- "Sil"
58882
+ "Delete"
58852
58883
  ] }),
58853
58884
  /* @__PURE__ */ jsx(MoonUIButtonPro, { variant: "ghost", size: "sm", onClick: onClearSelection, children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }) })
58854
58885
  ] })
@@ -58870,37 +58901,45 @@ var FilePreviewModal = ({
58870
58901
  /* @__PURE__ */ jsx(MoonUIBadgePro, { variant: "secondary", children: formatFileSize(file.file.size) })
58871
58902
  ] }) }),
58872
58903
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center p-4 bg-muted/20 rounded-lg", children: [
58873
- file.preview.type === "image" && /* @__PURE__ */ jsx(
58874
- "img",
58875
- {
58876
- src: file.preview.url,
58877
- alt: file.file.name,
58878
- className: "max-w-full max-h-[60vh] object-contain rounded"
58879
- }
58880
- ),
58881
- file.preview.type === "video" && /* @__PURE__ */ jsx(
58904
+ file.preview.type === "image" && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
58905
+ /* @__PURE__ */ jsx(
58906
+ "img",
58907
+ {
58908
+ src: file.preview.url,
58909
+ alt: file.file.name,
58910
+ className: "max-w-full max-h-[60vh] object-contain rounded shadow-lg",
58911
+ loading: "eager"
58912
+ }
58913
+ ),
58914
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-background/80 rounded opacity-0 pointer-events-none transition-opacity duration-300", children: /* @__PURE__ */ jsx(Loader2, { className: "h-8 w-8 animate-spin text-muted-foreground" }) })
58915
+ ] }),
58916
+ file.preview.type === "video" && /* @__PURE__ */ jsx("div", { className: "relative w-full max-w-4xl", children: /* @__PURE__ */ jsx(
58882
58917
  "video",
58883
58918
  {
58884
58919
  src: file.preview.url,
58885
58920
  controls: true,
58886
- className: "max-w-full max-h-[60vh] rounded"
58921
+ className: "w-full max-h-[60vh] rounded shadow-lg",
58922
+ poster: file.preview.thumbnail
58887
58923
  }
58888
- ),
58889
- file.preview.type === "audio" && /* @__PURE__ */ jsx("div", { className: "w-full max-w-md", children: /* @__PURE__ */ jsx("audio", { src: file.preview.url, controls: true, className: "w-full" }) }),
58924
+ ) }),
58925
+ file.preview.type === "audio" && /* @__PURE__ */ jsx("div", { className: "w-full max-w-md space-y-4", children: /* @__PURE__ */ jsxs("div", { className: "p-8 bg-gradient-to-br from-primary/10 to-primary/5 rounded-lg", children: [
58926
+ /* @__PURE__ */ jsx(Music, { className: "h-16 w-16 mx-auto text-primary mb-4" }),
58927
+ /* @__PURE__ */ jsx("audio", { src: file.preview.url, controls: true, className: "w-full" })
58928
+ ] }) }),
58890
58929
  !["image", "video", "audio"].includes(file.preview.type) && /* @__PURE__ */ jsxs("div", { className: "text-center py-8", children: [
58891
58930
  getFileIcon(file.file.type, "lg"),
58892
- /* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: "\xD6nizleme mevcut de\u011Fil" })
58931
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: "Preview not available" })
58893
58932
  ] })
58894
58933
  ] }),
58895
58934
  file.preview.dimensions && /* @__PURE__ */ jsxs("div", { className: "flex gap-4 text-sm text-muted-foreground", children: [
58896
58935
  /* @__PURE__ */ jsxs("span", { children: [
58897
- "Boyutlar: ",
58936
+ "Dimensions: ",
58898
58937
  file.preview.dimensions.width,
58899
58938
  " \xD7 ",
58900
58939
  file.preview.dimensions.height
58901
58940
  ] }),
58902
58941
  file.preview.duration && /* @__PURE__ */ jsxs("span", { children: [
58903
- "S\xFCre: ",
58942
+ "Duration: ",
58904
58943
  formatTime(file.preview.duration)
58905
58944
  ] })
58906
58945
  ] })
@@ -58952,21 +58991,21 @@ var MoonUIFileUploadPro = t__default.forwardRef(
58952
58991
  return /* @__PURE__ */ jsx(MoonUICardPro, { className: cn("w-full", className), children: /* @__PURE__ */ jsx(MoonUICardContentPro, { className: "py-12 text-center", children: /* @__PURE__ */ jsxs("div", { className: "max-w-md mx-auto space-y-4", children: [
58953
58992
  /* @__PURE__ */ jsx("div", { className: "rounded-full bg-purple-100 dark:bg-purple-900/30 p-3 w-fit mx-auto", children: /* @__PURE__ */ jsx(Lock, { className: "h-6 w-6 text-purple-600 dark:text-purple-400" }) }),
58954
58993
  /* @__PURE__ */ jsxs("div", { children: [
58955
- /* @__PURE__ */ jsx("h3", { className: "font-semibold text-lg mb-2", children: "Pro \xD6zellik" }),
58956
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm mb-4", children: "Geli\u015Fmi\u015F Dosya Y\xFCkleme sadece MoonUI Pro abonelerine \xF6zeldir." }),
58994
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold text-lg mb-2", children: "Pro Feature" }),
58995
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm mb-4", children: "Advanced File Upload is exclusive to MoonUI Pro subscribers." }),
58957
58996
  /* @__PURE__ */ jsx("div", { className: "flex gap-3 justify-center", children: /* @__PURE__ */ jsx("a", { href: "/pricing", children: /* @__PURE__ */ jsxs(MoonUIButtonPro, { size: "sm", children: [
58958
58997
  /* @__PURE__ */ jsx(Sparkles, { className: "mr-2 h-4 w-4" }),
58959
- "Pro'ya Y\xFCkseltin"
58998
+ "Upgrade to Pro"
58960
58999
  ] }) }) })
58961
59000
  ] })
58962
59001
  ] }) }) });
58963
59002
  }
58964
59003
  const validateFile = useCallback(async (file) => {
58965
59004
  if (file.size > maxSize) {
58966
- return `Dosya boyutu ${formatFileSize(maxSize)} limitini a\u015F\u0131yor`;
59005
+ return `File size exceeds ${formatFileSize(maxSize)} limit`;
58967
59006
  }
58968
59007
  if (allowedMimeTypes.length > 0 && !allowedMimeTypes.includes(file.type)) {
58969
- return `Dosya t\xFCr\xFC ${file.type} desteklenmiyor`;
59008
+ return `File type ${file.type} is not supported`;
58970
59009
  }
58971
59010
  if (accept !== "*") {
58972
59011
  const acceptTypes = accept.split(",").map((t2) => t2.trim());
@@ -58977,7 +59016,7 @@ var MoonUIFileUploadPro = t__default.forwardRef(
58977
59016
  return file.type === acceptType;
58978
59017
  });
58979
59018
  if (!isAccepted) {
58980
- return `Dosya t\xFCr\xFC kabul edilmiyor`;
59019
+ return `File type not accepted`;
58981
59020
  }
58982
59021
  }
58983
59022
  if (customValidation) {
@@ -59012,7 +59051,7 @@ var MoonUIFileUploadPro = t__default.forwardRef(
59012
59051
  };
59013
59052
  }
59014
59053
  } catch (error2) {
59015
- console.warn("\xD6nizleme olu\u015Fturulamad\u0131:", error2);
59054
+ console.warn("Failed to create preview:", error2);
59016
59055
  }
59017
59056
  return void 0;
59018
59057
  }, [showPreview, previewTypes]);
@@ -59090,14 +59129,14 @@ var MoonUIFileUploadPro = t__default.forwardRef(
59090
59129
  const fileArray = Array.from(fileList);
59091
59130
  setError(null);
59092
59131
  if (files.length + fileArray.length > maxFiles) {
59093
- setError(`Maksimum ${maxFiles} dosya y\xFCkleyebilirsiniz`);
59132
+ setError(`Maximum ${maxFiles} files allowed`);
59094
59133
  return;
59095
59134
  }
59096
59135
  if (maxTotalSize) {
59097
59136
  const currentSize = files.reduce((sum, f) => sum + f.file.size, 0);
59098
59137
  const newSize = fileArray.reduce((sum, f) => sum + f.size, 0);
59099
59138
  if (currentSize + newSize > maxTotalSize) {
59100
- setError(`Toplam dosya boyutu ${formatFileSize(maxTotalSize)} limitini a\u015F\u0131yor`);
59139
+ setError(`Total file size exceeds ${formatFileSize(maxTotalSize)} limit`);
59101
59140
  return;
59102
59141
  }
59103
59142
  }
@@ -59111,7 +59150,7 @@ var MoonUIFileUploadPro = t__default.forwardRef(
59111
59150
  }
59112
59151
  const isDuplicate = await checkDuplicate(file);
59113
59152
  if (isDuplicate) {
59114
- errors.push(`${file.name}: Dosya zaten mevcut`);
59153
+ errors.push(`${file.name}: File already exists`);
59115
59154
  continue;
59116
59155
  }
59117
59156
  validFiles.push(file);
@@ -59298,28 +59337,28 @@ var MoonUIFileUploadPro = t__default.forwardRef(
59298
59337
  }
59299
59338
  ),
59300
59339
  /* @__PURE__ */ jsxs("div", { children: [
59301
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: isDragOver ? "Dosyalar\u0131 buraya b\u0131rak\u0131n" : "Dosya Y\xFCkleyin" }),
59302
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Dosyalar\u0131 s\xFCr\xFCkleyip b\u0131rak\u0131n veya t\u0131klayarak se\xE7in" }),
59340
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: isDragOver ? "Drop files here" : "Upload Files" }),
59341
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Drag and drop files or click to select" }),
59303
59342
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-4 mt-3 text-xs text-muted-foreground", children: [
59304
59343
  /* @__PURE__ */ jsxs("span", { children: [
59305
- "Maks ",
59344
+ "Max ",
59306
59345
  maxFiles,
59307
- " dosya"
59346
+ " files"
59308
59347
  ] }),
59309
59348
  /* @__PURE__ */ jsx("span", { children: "\u2022" }),
59310
59349
  /* @__PURE__ */ jsxs("span", { children: [
59311
59350
  formatFileSize(maxSize),
59312
- " her dosya"
59351
+ " per file"
59313
59352
  ] }),
59314
59353
  resumable && /* @__PURE__ */ jsxs(Fragment, { children: [
59315
59354
  /* @__PURE__ */ jsx("span", { children: "\u2022" }),
59316
- /* @__PURE__ */ jsx("span", { children: "Devam ettirilebilir" })
59355
+ /* @__PURE__ */ jsx("span", { children: "Resumable" })
59317
59356
  ] })
59318
59357
  ] })
59319
59358
  ] }),
59320
59359
  /* @__PURE__ */ jsxs(MoonUIButtonPro, { variant: "outline", disabled, type: "button", children: [
59321
59360
  /* @__PURE__ */ jsx(Upload, { className: "mr-2 h-4 w-4" }),
59322
- "Dosya Se\xE7"
59361
+ "Select Files"
59323
59362
  ] })
59324
59363
  ] })
59325
59364
  ]
@@ -59358,7 +59397,7 @@ var MoonUIFileUploadPro = t__default.forwardRef(
59358
59397
  children: [
59359
59398
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
59360
59399
  /* @__PURE__ */ jsxs("h4", { className: "text-sm font-medium", children: [
59361
- "Y\xFCklenen Dosyalar (",
59400
+ "Uploaded Files (",
59362
59401
  files.length,
59363
59402
  ")"
59364
59403
  ] }),
@@ -59368,7 +59407,7 @@ var MoonUIFileUploadPro = t__default.forwardRef(
59368
59407
  variant: "ghost",
59369
59408
  size: "sm",
59370
59409
  onClick: selectedIds.length === files.length ? handleClearSelection : handleSelectAll,
59371
- children: selectedIds.length === files.length ? "Se\xE7imi Temizle" : "T\xFCm\xFCn\xFC Se\xE7"
59410
+ children: selectedIds.length === files.length ? "Clear Selection" : "Select All"
59372
59411
  }
59373
59412
  )
59374
59413
  ] }),
@@ -59522,7 +59561,7 @@ var FileUploadItem = ({
59522
59561
  file.status === "success" && /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3 w-3 mr-1" }),
59523
59562
  file.status === "error" && /* @__PURE__ */ jsx(AlertCircle, { className: "h-3 w-3 mr-1" }),
59524
59563
  file.status === "paused" && /* @__PURE__ */ jsx(Pause, { className: "h-3 w-3 mr-1" }),
59525
- file.status === "pending" ? "Bekliyor" : file.status === "uploading" ? "Y\xFCkleniyor" : file.status === "paused" ? "Duraklat\u0131ld\u0131" : file.status === "success" ? "Tamamland\u0131" : file.status === "error" ? "Hata" : "\u0130ptal"
59564
+ file.status === "pending" ? "Pending" : file.status === "uploading" ? "Uploading" : file.status === "paused" ? "Paused" : file.status === "success" ? "Completed" : file.status === "error" ? "Error" : "Cancelled"
59526
59565
  ]
59527
59566
  }
59528
59567
  )
@@ -59539,7 +59578,7 @@ var FileUploadItem = ({
59539
59578
  ] }),
59540
59579
  file.estimatedTime && file.estimatedTime > 0 && /* @__PURE__ */ jsxs("span", { children: [
59541
59580
  formatTime(file.estimatedTime),
59542
- " kald\u0131"
59581
+ " remaining"
59543
59582
  ] })
59544
59583
  ] }),
59545
59584
  /* @__PURE__ */ jsx(MoonUIProgressPro, { value: file.progress, className: "h-1" })
@@ -59583,19 +59622,19 @@ var FileUploadItem = ({
59583
59622
  /* @__PURE__ */ jsxs(MoonUIDropdownMenuContentPro, { align: "end", children: [
59584
59623
  canPreview && /* @__PURE__ */ jsxs(MoonUIDropdownMenuItemPro, { onClick: onPreview, children: [
59585
59624
  /* @__PURE__ */ jsx(Eye, { className: "mr-2 h-4 w-4" }),
59586
- "\xD6nizle"
59625
+ "Preview"
59587
59626
  ] }),
59588
59627
  /* @__PURE__ */ jsxs(MoonUIDropdownMenuItemPro, { children: [
59589
59628
  /* @__PURE__ */ jsx(Copy, { className: "mr-2 h-4 w-4" }),
59590
- "Linki Kopyala"
59629
+ "Copy Link"
59591
59630
  ] }),
59592
59631
  /* @__PURE__ */ jsxs(MoonUIDropdownMenuItemPro, { children: [
59593
59632
  /* @__PURE__ */ jsx(Share, { className: "mr-2 h-4 w-4" }),
59594
- "Payla\u015F"
59633
+ "Share"
59595
59634
  ] }),
59596
59635
  /* @__PURE__ */ jsxs(MoonUIDropdownMenuItemPro, { onClick: onRemove, className: "text-destructive", children: [
59597
59636
  /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
59598
- "Sil"
59637
+ "Delete"
59599
59638
  ] })
59600
59639
  ] })
59601
59640
  ] })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui-pro",
3
- "version": "2.11.0",
3
+ "version": "2.11.1",
4
4
  "description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -78,6 +78,28 @@ export interface FileUploadProProps {
78
78
  endpoint?: string
79
79
  headers?: Record<string, string>
80
80
 
81
+ // Service integrations
82
+ serviceConfig?: {
83
+ type: 'aws-s3' | 'cloudinary' | 'firebase' | 'custom'
84
+ awsConfig?: {
85
+ region: string
86
+ bucket: string
87
+ accessKeyId?: string
88
+ secretAccessKey?: string
89
+ sessionToken?: string
90
+ }
91
+ cloudinaryConfig?: {
92
+ cloudName: string
93
+ uploadPreset: string
94
+ apiKey?: string
95
+ apiSecret?: string
96
+ }
97
+ firebaseConfig?: {
98
+ storageBucket: string
99
+ folder?: string
100
+ }
101
+ }
102
+
81
103
  // Callbacks
82
104
  onUpload?: (files: FileUploadItem[]) => Promise<void>
83
105
  onProgress?: (fileId: string, progress: number) => void
@@ -200,26 +222,67 @@ const generateFileHash = async (file: File): Promise<string> => {
200
222
 
201
223
  const createImagePreview = (file: File): Promise<FilePreview> => {
202
224
  return new Promise((resolve) => {
203
- const img = new Image()
204
- const url = URL.createObjectURL(file)
225
+ const reader = new FileReader()
205
226
 
206
- img.onload = () => {
207
- resolve({
208
- type: 'image',
209
- url,
210
- thumbnail: url,
211
- dimensions: { width: img.width, height: img.height }
212
- })
227
+ reader.onload = (e) => {
228
+ const result = e.target?.result as string
229
+ const img = new Image()
230
+
231
+ img.onload = () => {
232
+ // Create high-quality thumbnail
233
+ const canvas = document.createElement('canvas')
234
+ const ctx = canvas.getContext('2d')!
235
+
236
+ // Calculate thumbnail dimensions (max 400px)
237
+ const maxThumbSize = 400
238
+ let thumbWidth = img.width
239
+ let thumbHeight = img.height
240
+
241
+ if (thumbWidth > maxThumbSize || thumbHeight > maxThumbSize) {
242
+ const aspectRatio = img.width / img.height
243
+ if (aspectRatio > 1) {
244
+ thumbWidth = maxThumbSize
245
+ thumbHeight = maxThumbSize / aspectRatio
246
+ } else {
247
+ thumbHeight = maxThumbSize
248
+ thumbWidth = maxThumbSize * aspectRatio
249
+ }
250
+ }
251
+
252
+ canvas.width = thumbWidth
253
+ canvas.height = thumbHeight
254
+ ctx.drawImage(img, 0, 0, thumbWidth, thumbHeight)
255
+
256
+ const thumbnail = canvas.toDataURL('image/jpeg', 0.9)
257
+
258
+ resolve({
259
+ type: 'image',
260
+ url: result,
261
+ thumbnail,
262
+ dimensions: { width: img.width, height: img.height }
263
+ })
264
+ }
265
+
266
+ img.onerror = () => {
267
+ resolve({
268
+ type: 'image',
269
+ url: result,
270
+ thumbnail: result
271
+ })
272
+ }
273
+
274
+ img.src = result
213
275
  }
214
276
 
215
- img.onerror = () => {
277
+ reader.onerror = () => {
278
+ const url = URL.createObjectURL(file)
216
279
  resolve({
217
280
  type: 'image',
218
281
  url
219
282
  })
220
283
  }
221
284
 
222
- img.src = url
285
+ reader.readAsDataURL(file)
223
286
  })
224
287
  }
225
288
 
@@ -284,15 +347,15 @@ const BulkActions = ({
284
347
  exit={{ opacity: 0, y: -10 }}
285
348
  className="flex items-center gap-2 p-3 bg-primary/5 rounded-lg border"
286
349
  >
287
- <span className="text-sm font-medium">{selectedIds.length} dosya seçildi</span>
350
+ <span className="text-sm font-medium">{selectedIds.length} files selected</span>
288
351
  <div className="flex gap-1 ml-auto">
289
352
  <Button variant="ghost" size="sm" onClick={onBulkDownload}>
290
353
  <Download className="h-4 w-4 mr-1" />
291
- İndir
354
+ Download
292
355
  </Button>
293
356
  <Button variant="ghost" size="sm" onClick={onBulkRemove}>
294
357
  <Trash2 className="h-4 w-4 mr-1" />
295
- Sil
358
+ Delete
296
359
  </Button>
297
360
  <Button variant="ghost" size="sm" onClick={onClearSelection}>
298
361
  <X className="h-4 w-4" />
@@ -329,40 +392,53 @@ const FilePreviewModal = ({
329
392
 
330
393
  <div className="flex items-center justify-center p-4 bg-muted/20 rounded-lg">
331
394
  {file.preview.type === 'image' && (
332
- <img
333
- src={file.preview.url}
334
- alt={file.file.name}
335
- className="max-w-full max-h-[60vh] object-contain rounded"
336
- />
395
+ <div className="relative">
396
+ <img
397
+ src={file.preview.url}
398
+ alt={file.file.name}
399
+ className="max-w-full max-h-[60vh] object-contain rounded shadow-lg"
400
+ loading="eager"
401
+ />
402
+ {/* Image loading indicator */}
403
+ <div className="absolute inset-0 flex items-center justify-center bg-background/80 rounded opacity-0 pointer-events-none transition-opacity duration-300">
404
+ <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
405
+ </div>
406
+ </div>
337
407
  )}
338
408
 
339
409
  {file.preview.type === 'video' && (
340
- <video
341
- src={file.preview.url}
342
- controls
343
- className="max-w-full max-h-[60vh] rounded"
344
- />
410
+ <div className="relative w-full max-w-4xl">
411
+ <video
412
+ src={file.preview.url}
413
+ controls
414
+ className="w-full max-h-[60vh] rounded shadow-lg"
415
+ poster={file.preview.thumbnail}
416
+ />
417
+ </div>
345
418
  )}
346
419
 
347
420
  {file.preview.type === 'audio' && (
348
- <div className="w-full max-w-md">
349
- <audio src={file.preview.url} controls className="w-full" />
421
+ <div className="w-full max-w-md space-y-4">
422
+ <div className="p-8 bg-gradient-to-br from-primary/10 to-primary/5 rounded-lg">
423
+ <Music className="h-16 w-16 mx-auto text-primary mb-4" />
424
+ <audio src={file.preview.url} controls className="w-full" />
425
+ </div>
350
426
  </div>
351
427
  )}
352
428
 
353
429
  {!['image', 'video', 'audio'].includes(file.preview.type) && (
354
430
  <div className="text-center py-8">
355
431
  {getFileIcon(file.file.type, 'lg')}
356
- <p className="mt-2 text-muted-foreground">Önizleme mevcut değil</p>
432
+ <p className="mt-2 text-muted-foreground">Preview not available</p>
357
433
  </div>
358
434
  )}
359
435
  </div>
360
436
 
361
437
  {file.preview.dimensions && (
362
438
  <div className="flex gap-4 text-sm text-muted-foreground">
363
- <span>Boyutlar: {file.preview.dimensions.width} × {file.preview.dimensions.height}</span>
439
+ <span>Dimensions: {file.preview.dimensions.width} × {file.preview.dimensions.height}</span>
364
440
  {file.preview.duration && (
365
- <span>Süre: {formatTime(file.preview.duration)}</span>
441
+ <span>Duration: {formatTime(file.preview.duration)}</span>
366
442
  )}
367
443
  </div>
368
444
  )}
@@ -428,15 +504,15 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
428
504
  <Lock className="h-6 w-6 text-purple-600 dark:text-purple-400" />
429
505
  </div>
430
506
  <div>
431
- <h3 className="font-semibold text-lg mb-2">Pro Özellik</h3>
507
+ <h3 className="font-semibold text-lg mb-2">Pro Feature</h3>
432
508
  <p className="text-muted-foreground text-sm mb-4">
433
- Gelişmiş Dosya Yükleme sadece MoonUI Pro abonelerine özeldir.
509
+ Advanced File Upload is exclusive to MoonUI Pro subscribers.
434
510
  </p>
435
511
  <div className="flex gap-3 justify-center">
436
512
  <a href="/pricing">
437
513
  <Button size="sm">
438
514
  <Sparkles className="mr-2 h-4 w-4" />
439
- Pro'ya Yükseltin
515
+ Upgrade to Pro
440
516
  </Button>
441
517
  </a>
442
518
  </div>
@@ -451,12 +527,12 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
451
527
  const validateFile = useCallback(async (file: File): Promise<string | null> => {
452
528
  // Boyut kontrolü
453
529
  if (file.size > maxSize) {
454
- return `Dosya boyutu ${formatFileSize(maxSize)} limitini aşıyor`
530
+ return `File size exceeds ${formatFileSize(maxSize)} limit`
455
531
  }
456
532
 
457
533
  // MIME type kontrolü
458
534
  if (allowedMimeTypes.length > 0 && !allowedMimeTypes.includes(file.type)) {
459
- return `Dosya türü ${file.type} desteklenmiyor`
535
+ return `File type ${file.type} is not supported`
460
536
  }
461
537
 
462
538
  // Accept kontrolü
@@ -470,7 +546,7 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
470
546
  })
471
547
 
472
548
  if (!isAccepted) {
473
- return `Dosya türü kabul edilmiyor`
549
+ return `File type not accepted`
474
550
  }
475
551
  }
476
552
 
@@ -512,7 +588,7 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
512
588
  }
513
589
  }
514
590
  } catch (error) {
515
- console.warn('Önizleme oluşturulamadı:', error)
591
+ console.warn('Failed to create preview:', error)
516
592
  }
517
593
 
518
594
  return undefined
@@ -630,7 +706,7 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
630
706
 
631
707
  // Toplam dosya sayısı kontrolü
632
708
  if (files.length + fileArray.length > maxFiles) {
633
- setError(`Maksimum ${maxFiles} dosya yükleyebilirsiniz`)
709
+ setError(`Maximum ${maxFiles} files allowed`)
634
710
  return
635
711
  }
636
712
 
@@ -640,7 +716,7 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
640
716
  const newSize = fileArray.reduce((sum, f) => sum + f.size, 0)
641
717
 
642
718
  if (currentSize + newSize > maxTotalSize) {
643
- setError(`Toplam dosya boyutu ${formatFileSize(maxTotalSize)} limitini aşıyor`)
719
+ setError(`Total file size exceeds ${formatFileSize(maxTotalSize)} limit`)
644
720
  return
645
721
  }
646
722
  }
@@ -658,7 +734,7 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
658
734
 
659
735
  const isDuplicate = await checkDuplicate(file)
660
736
  if (isDuplicate) {
661
- errors.push(`${file.name}: Dosya zaten mevcut`)
737
+ errors.push(`${file.name}: File already exists`)
662
738
  continue
663
739
  }
664
740
 
@@ -886,19 +962,19 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
886
962
 
887
963
  <div>
888
964
  <h3 className="text-lg font-semibold">
889
- {isDragOver ? 'Dosyaları buraya bırakın' : 'Dosya Yükleyin'}
965
+ {isDragOver ? 'Drop files here' : 'Upload Files'}
890
966
  </h3>
891
967
  <p className="text-sm text-muted-foreground mt-1">
892
- Dosyaları sürükleyip bırakın veya tıklayarak seçin
968
+ Drag and drop files or click to select
893
969
  </p>
894
970
  <div className="flex items-center justify-center gap-4 mt-3 text-xs text-muted-foreground">
895
- <span>Maks {maxFiles} dosya</span>
971
+ <span>Max {maxFiles} files</span>
896
972
  <span>•</span>
897
- <span>{formatFileSize(maxSize)} her dosya</span>
973
+ <span>{formatFileSize(maxSize)} per file</span>
898
974
  {resumable && (
899
975
  <>
900
976
  <span>•</span>
901
- <span>Devam ettirilebilir</span>
977
+ <span>Resumable</span>
902
978
  </>
903
979
  )}
904
980
  </div>
@@ -906,7 +982,7 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
906
982
 
907
983
  <Button variant="outline" disabled={disabled} type="button">
908
984
  <Upload className="mr-2 h-4 w-4" />
909
- Dosya Seç
985
+ Select Files
910
986
  </Button>
911
987
  </div>
912
988
  </motion.div>
@@ -946,7 +1022,7 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
946
1022
  {/* List Header */}
947
1023
  <div className="flex items-center justify-between">
948
1024
  <h4 className="text-sm font-medium">
949
- Yüklenen Dosyalar ({files.length})
1025
+ Uploaded Files ({files.length})
950
1026
  </h4>
951
1027
  {allowBulkOperations && files.length > 1 && (
952
1028
  <Button
@@ -954,7 +1030,7 @@ export const MoonUIFileUploadPro = React.forwardRef<HTMLDivElement, FileUploadPr
954
1030
  size="sm"
955
1031
  onClick={selectedIds.length === files.length ? handleClearSelection : handleSelectAll}
956
1032
  >
957
- {selectedIds.length === files.length ? 'Seçimi Temizle' : 'Tümünü Seç'}
1033
+ {selectedIds.length === files.length ? 'Clear Selection' : 'Select All'}
958
1034
  </Button>
959
1035
  )}
960
1036
  </div>
@@ -1152,11 +1228,11 @@ const FileUploadItem = ({
1152
1228
  {file.status === 'success' && <CheckCircle2 className="h-3 w-3 mr-1" />}
1153
1229
  {file.status === 'error' && <AlertCircle className="h-3 w-3 mr-1" />}
1154
1230
  {file.status === 'paused' && <Pause className="h-3 w-3 mr-1" />}
1155
- {file.status === 'pending' ? 'Bekliyor' :
1156
- file.status === 'uploading' ? 'Yükleniyor' :
1157
- file.status === 'paused' ? 'Duraklatıldı' :
1158
- file.status === 'success' ? 'Tamamlandı' :
1159
- file.status === 'error' ? 'Hata' : 'İptal'}
1231
+ {file.status === 'pending' ? 'Pending' :
1232
+ file.status === 'uploading' ? 'Uploading' :
1233
+ file.status === 'paused' ? 'Paused' :
1234
+ file.status === 'success' ? 'Completed' :
1235
+ file.status === 'error' ? 'Error' : 'Cancelled'}
1160
1236
  </Badge>
1161
1237
  </div>
1162
1238
 
@@ -1169,7 +1245,7 @@ const FileUploadItem = ({
1169
1245
  <span>{formatFileSize(file.speed)}/s</span>
1170
1246
  )}
1171
1247
  {file.estimatedTime && file.estimatedTime > 0 && (
1172
- <span>{formatTime(file.estimatedTime)} kaldı</span>
1248
+ <span>{formatTime(file.estimatedTime)} remaining</span>
1173
1249
  )}
1174
1250
  </div>
1175
1251
  <Progress value={file.progress} className="h-1" />
@@ -1232,20 +1308,20 @@ const FileUploadItem = ({
1232
1308
  {canPreview && (
1233
1309
  <DropdownMenuItem onClick={onPreview}>
1234
1310
  <Eye className="mr-2 h-4 w-4" />
1235
- Önizle
1311
+ Preview
1236
1312
  </DropdownMenuItem>
1237
1313
  )}
1238
1314
  <DropdownMenuItem>
1239
1315
  <Copy className="mr-2 h-4 w-4" />
1240
- Linki Kopyala
1316
+ Copy Link
1241
1317
  </DropdownMenuItem>
1242
1318
  <DropdownMenuItem>
1243
1319
  <Share className="mr-2 h-4 w-4" />
1244
- Paylaş
1320
+ Share
1245
1321
  </DropdownMenuItem>
1246
1322
  <DropdownMenuItem onClick={onRemove} className="text-destructive">
1247
1323
  <Trash2 className="mr-2 h-4 w-4" />
1248
- Sil
1324
+ Delete
1249
1325
  </DropdownMenuItem>
1250
1326
  </DropdownMenuContent>
1251
1327
  </DropdownMenu>