@sparkstudio/storage-ui 1.0.28 → 1.0.30
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.cjs +999 -275
- package/dist/index.d.cts +85 -18
- package/dist/index.d.ts +85 -18
- package/dist/index.js +990 -264
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -23,9 +23,13 @@ __export(index_exports, {
|
|
|
23
23
|
AWSPresignedUrlDTO: () => AWSPresignedUrlDTO,
|
|
24
24
|
Container: () => Container,
|
|
25
25
|
ContainerDTO: () => ContainerDTO,
|
|
26
|
+
ContainerIdGridPanel: () => ContainerIdGridPanel,
|
|
26
27
|
ContainerType: () => ContainerType,
|
|
27
28
|
ContainerUploadPanel: () => ContainerUploadPanel,
|
|
28
29
|
DesktopFileIcon: () => DesktopFileIcon,
|
|
30
|
+
FileGridUploadPanel: () => FileGridUploadPanel,
|
|
31
|
+
FileIconCard: () => FileIconCard,
|
|
32
|
+
FileIconGrid: () => FileIconGrid,
|
|
29
33
|
Home: () => Home,
|
|
30
34
|
HomeView: () => HomeView,
|
|
31
35
|
S3: () => S3,
|
|
@@ -276,54 +280,11 @@ var ContainerType = /* @__PURE__ */ ((ContainerType2) => {
|
|
|
276
280
|
return ContainerType2;
|
|
277
281
|
})(ContainerType || {});
|
|
278
282
|
|
|
279
|
-
// src/components/
|
|
280
|
-
var import_react7 = require("react");
|
|
281
|
-
|
|
282
|
-
// src/components/UploadContainer.tsx
|
|
283
|
+
// src/components/ContainerIdGridPanel.tsx
|
|
283
284
|
var import_react5 = require("react");
|
|
284
285
|
|
|
285
|
-
// src/components/UploadDropzone.tsx
|
|
286
|
-
var import_react = require("react");
|
|
287
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
288
|
-
var UploadDropzone = ({
|
|
289
|
-
isDragging,
|
|
290
|
-
onDragOver,
|
|
291
|
-
onDragLeave,
|
|
292
|
-
onDrop,
|
|
293
|
-
className = "",
|
|
294
|
-
style,
|
|
295
|
-
children
|
|
296
|
-
}) => {
|
|
297
|
-
const baseClass = "rounded-3 d-flex flex-column align-items-center justify-content-center";
|
|
298
|
-
const stateClass = isDragging ? "bg-body-secondary border-dashed border-2 border-secondary" : "bg-body-trasparent border-solid border-transparent border-2";
|
|
299
|
-
const combinedClassName = `${baseClass} ${stateClass} ${className}`.trim();
|
|
300
|
-
const handleDragOver = (e) => {
|
|
301
|
-
e.preventDefault();
|
|
302
|
-
onDragOver?.(e);
|
|
303
|
-
};
|
|
304
|
-
const handleDragLeave = (e) => {
|
|
305
|
-
e.preventDefault();
|
|
306
|
-
onDragLeave?.(e);
|
|
307
|
-
};
|
|
308
|
-
const handleDrop = (e) => {
|
|
309
|
-
e.preventDefault();
|
|
310
|
-
onDrop?.(e);
|
|
311
|
-
};
|
|
312
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
313
|
-
"div",
|
|
314
|
-
{
|
|
315
|
-
className: combinedClassName,
|
|
316
|
-
style: { minHeight: "140px", ...style },
|
|
317
|
-
onDragOver: handleDragOver,
|
|
318
|
-
onDragLeave: handleDragLeave,
|
|
319
|
-
onDrop: handleDrop,
|
|
320
|
-
children
|
|
321
|
-
}
|
|
322
|
-
);
|
|
323
|
-
};
|
|
324
|
-
|
|
325
286
|
// src/hooks/UseUploadManager.ts
|
|
326
|
-
var
|
|
287
|
+
var import_react = require("react");
|
|
327
288
|
|
|
328
289
|
// src/engines/UploadFileToS3.ts
|
|
329
290
|
async function UploadFileToS3(file, presignedUrl, onProgress, maxRetries = 3) {
|
|
@@ -380,14 +341,14 @@ function UseUploadManager({
|
|
|
380
341
|
onUploadComplete,
|
|
381
342
|
onUploadError
|
|
382
343
|
}) {
|
|
383
|
-
const [uploads, setUploads] = (0,
|
|
344
|
+
const [uploads, setUploads] = (0, import_react.useState)([]);
|
|
384
345
|
const createUploadStates = (files) => Array.from(files).map((file) => ({
|
|
385
346
|
id: `${file.name}-${file.size}-${file.lastModified}-${Math.random()}`,
|
|
386
347
|
file,
|
|
387
348
|
progress: 0,
|
|
388
349
|
status: "pending"
|
|
389
350
|
}));
|
|
390
|
-
const uploadFile = (0,
|
|
351
|
+
const uploadFile = (0, import_react.useCallback)(
|
|
391
352
|
async (upload) => {
|
|
392
353
|
setUploads(
|
|
393
354
|
(prev) => prev.map(
|
|
@@ -453,7 +414,7 @@ function UseUploadManager({
|
|
|
453
414
|
}
|
|
454
415
|
throw lastError instanceof Error ? lastError : new Error("Failed to fetch presigned URL");
|
|
455
416
|
}
|
|
456
|
-
const startUploadsIfNeeded = (0,
|
|
417
|
+
const startUploadsIfNeeded = (0, import_react.useCallback)(
|
|
457
418
|
(files) => {
|
|
458
419
|
if (!autoUpload || !getPresignedUrl) return;
|
|
459
420
|
const newUploads = createUploadStates(files);
|
|
@@ -464,7 +425,7 @@ function UseUploadManager({
|
|
|
464
425
|
},
|
|
465
426
|
[autoUpload, getPresignedUrl, uploadFile]
|
|
466
427
|
);
|
|
467
|
-
const resetUploads = (0,
|
|
428
|
+
const resetUploads = (0, import_react.useCallback)(() => setUploads([]), []);
|
|
468
429
|
return {
|
|
469
430
|
uploads,
|
|
470
431
|
startUploadsIfNeeded,
|
|
@@ -473,19 +434,19 @@ function UseUploadManager({
|
|
|
473
434
|
}
|
|
474
435
|
|
|
475
436
|
// src/components/UploadProgressList.tsx
|
|
476
|
-
var
|
|
477
|
-
var
|
|
437
|
+
var import_react2 = require("react");
|
|
438
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
478
439
|
var UploadProgressList = ({
|
|
479
440
|
uploads
|
|
480
441
|
}) => {
|
|
481
442
|
const visibleUploads = uploads.filter((u) => u.status !== "success");
|
|
482
443
|
if (visibleUploads.length === 0) return null;
|
|
483
|
-
return /* @__PURE__ */ (0,
|
|
444
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
484
445
|
"div",
|
|
485
446
|
{
|
|
486
447
|
className: "w-100 d-flex flex-wrap gap-4 align-content-start mt-3",
|
|
487
448
|
style: { minHeight: "80px" },
|
|
488
|
-
children: visibleUploads.map((u) => /* @__PURE__ */ (0,
|
|
449
|
+
children: visibleUploads.map((u) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
489
450
|
"div",
|
|
490
451
|
{
|
|
491
452
|
className: "d-flex flex-column align-items-center",
|
|
@@ -495,7 +456,7 @@ var UploadProgressList = ({
|
|
|
495
456
|
},
|
|
496
457
|
title: u.file.name,
|
|
497
458
|
children: [
|
|
498
|
-
/* @__PURE__ */ (0,
|
|
459
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
499
460
|
"div",
|
|
500
461
|
{
|
|
501
462
|
className: "border rounded-3 d-flex align-items-center justify-content-center mb-1 shadow-sm position-relative",
|
|
@@ -504,20 +465,20 @@ var UploadProgressList = ({
|
|
|
504
465
|
height: 64
|
|
505
466
|
},
|
|
506
467
|
children: [
|
|
507
|
-
/* @__PURE__ */ (0,
|
|
508
|
-
u.status === "uploading" && /* @__PURE__ */ (0,
|
|
468
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "bi bi-file-earmark fs-2" }),
|
|
469
|
+
u.status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
509
470
|
"div",
|
|
510
471
|
{
|
|
511
472
|
className: "position-absolute top-50 start-50 translate-middle",
|
|
512
473
|
style: { pointerEvents: "none" },
|
|
513
|
-
children: /* @__PURE__ */ (0,
|
|
474
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "spinner-border spinner-border-sm text-primary" })
|
|
514
475
|
}
|
|
515
476
|
),
|
|
516
|
-
u.status === "error" && /* @__PURE__ */ (0,
|
|
477
|
+
u.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger", children: "!" })
|
|
517
478
|
]
|
|
518
479
|
}
|
|
519
480
|
),
|
|
520
|
-
/* @__PURE__ */ (0,
|
|
481
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
521
482
|
"div",
|
|
522
483
|
{
|
|
523
484
|
className: "small text-center text-truncate",
|
|
@@ -525,7 +486,7 @@ var UploadProgressList = ({
|
|
|
525
486
|
children: u.file.name
|
|
526
487
|
}
|
|
527
488
|
),
|
|
528
|
-
/* @__PURE__ */ (0,
|
|
489
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-100 mt-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "progress", style: { height: "4px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
529
490
|
"div",
|
|
530
491
|
{
|
|
531
492
|
className: "progress-bar " + (u.status === "error" ? "bg-danger" : ""),
|
|
@@ -536,7 +497,7 @@ var UploadProgressList = ({
|
|
|
536
497
|
"aria-valuemax": 100
|
|
537
498
|
}
|
|
538
499
|
) }) }),
|
|
539
|
-
u.status === "error" && /* @__PURE__ */ (0,
|
|
500
|
+
u.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
540
501
|
"div",
|
|
541
502
|
{
|
|
542
503
|
className: "text-danger small mt-1 text-center",
|
|
@@ -552,11 +513,558 @@ var UploadProgressList = ({
|
|
|
552
513
|
);
|
|
553
514
|
};
|
|
554
515
|
|
|
516
|
+
// src/components/FileIconGrid.tsx
|
|
517
|
+
var import_react4 = require("react");
|
|
518
|
+
|
|
519
|
+
// src/components/FileIconCard.tsx
|
|
520
|
+
var import_react3 = require("react");
|
|
521
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
522
|
+
function sanitizeIconHtml(input) {
|
|
523
|
+
return input.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "").replace(/\son\w+="[^"]*"/gi, "").replace(/\son\w+='[^']*'/gi, "").replace(/javascript:/gi, "");
|
|
524
|
+
}
|
|
525
|
+
var FileIconCard = (props) => {
|
|
526
|
+
const {
|
|
527
|
+
file,
|
|
528
|
+
deleteDisabled,
|
|
529
|
+
selected,
|
|
530
|
+
onSelect,
|
|
531
|
+
onDeleteFile,
|
|
532
|
+
onClickFile,
|
|
533
|
+
icon,
|
|
534
|
+
iconHtml,
|
|
535
|
+
title,
|
|
536
|
+
className
|
|
537
|
+
} = props;
|
|
538
|
+
const [deleting, setDeleting] = (0, import_react3.useState)(false);
|
|
539
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
540
|
+
const safeIconHtml = (0, import_react3.useMemo)(() => {
|
|
541
|
+
if (!iconHtml) return null;
|
|
542
|
+
return sanitizeIconHtml(iconHtml);
|
|
543
|
+
}, [iconHtml]);
|
|
544
|
+
const canDelete = !!onDeleteFile && !deleteDisabled && !deleting;
|
|
545
|
+
const handleClick = () => {
|
|
546
|
+
onSelect?.(file);
|
|
547
|
+
onClickFile?.(file);
|
|
548
|
+
};
|
|
549
|
+
const handleDelete = async (ev) => {
|
|
550
|
+
ev.stopPropagation();
|
|
551
|
+
if (!canDelete) return;
|
|
552
|
+
setError(null);
|
|
553
|
+
setDeleting(true);
|
|
554
|
+
try {
|
|
555
|
+
await onDeleteFile?.(file);
|
|
556
|
+
} catch (e) {
|
|
557
|
+
setError(e instanceof Error ? e.message : "Delete failed");
|
|
558
|
+
} finally {
|
|
559
|
+
setDeleting(false);
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
const borderColor = selected ? "rgba(13,110,253,0.8)" : "rgba(0,0,0,0.12)";
|
|
563
|
+
const boxShadow = selected ? "0 0 0 3px rgba(13,110,253,0.2)" : "none";
|
|
564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
565
|
+
"div",
|
|
566
|
+
{
|
|
567
|
+
className: [
|
|
568
|
+
"file-icon-card",
|
|
569
|
+
deleteDisabled ? "is-delete-disabled" : "",
|
|
570
|
+
deleting ? "is-deleting" : "",
|
|
571
|
+
className ?? ""
|
|
572
|
+
].join(" "),
|
|
573
|
+
role: "button",
|
|
574
|
+
tabIndex: 0,
|
|
575
|
+
onClick: handleClick,
|
|
576
|
+
onKeyDown: (e) => {
|
|
577
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
578
|
+
e.preventDefault();
|
|
579
|
+
handleClick();
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
title: title ?? file.Name ?? "File",
|
|
583
|
+
style: {
|
|
584
|
+
display: "flex",
|
|
585
|
+
alignItems: "center",
|
|
586
|
+
gap: 12,
|
|
587
|
+
padding: 12,
|
|
588
|
+
border: `1px solid ${borderColor}`,
|
|
589
|
+
boxShadow,
|
|
590
|
+
borderRadius: 12,
|
|
591
|
+
userSelect: "none",
|
|
592
|
+
cursor: "pointer",
|
|
593
|
+
background: "white"
|
|
594
|
+
},
|
|
595
|
+
children: [
|
|
596
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
597
|
+
"div",
|
|
598
|
+
{
|
|
599
|
+
style: {
|
|
600
|
+
width: 44,
|
|
601
|
+
height: 44,
|
|
602
|
+
borderRadius: 10,
|
|
603
|
+
display: "grid",
|
|
604
|
+
placeItems: "center",
|
|
605
|
+
background: "rgba(0,0,0,0.04)",
|
|
606
|
+
flex: "0 0 auto"
|
|
607
|
+
},
|
|
608
|
+
children: icon ? icon : safeIconHtml ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": true, dangerouslySetInnerHTML: { __html: safeIconHtml } }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DefaultFileIcon, {})
|
|
609
|
+
}
|
|
610
|
+
),
|
|
611
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { minWidth: 0, flex: "1 1 auto" }, children: [
|
|
612
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
613
|
+
"div",
|
|
614
|
+
{
|
|
615
|
+
style: {
|
|
616
|
+
fontWeight: 600,
|
|
617
|
+
overflow: "hidden",
|
|
618
|
+
textOverflow: "ellipsis",
|
|
619
|
+
whiteSpace: "nowrap"
|
|
620
|
+
},
|
|
621
|
+
children: title ?? file.Name ?? "Untitled"
|
|
622
|
+
}
|
|
623
|
+
),
|
|
624
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: 12, opacity: 0.7 }, children: typeof file.FileSize === "number" ? formatBytes(file.FileSize) : "" }),
|
|
625
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: 12, color: "crimson", marginTop: 4 }, children: error }) : null
|
|
626
|
+
] }),
|
|
627
|
+
!deleteDisabled ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
628
|
+
"button",
|
|
629
|
+
{
|
|
630
|
+
type: "button",
|
|
631
|
+
onClick: handleDelete,
|
|
632
|
+
disabled: !canDelete,
|
|
633
|
+
"aria-label": "Delete file",
|
|
634
|
+
style: {
|
|
635
|
+
border: "1px solid rgba(0,0,0,0.18)",
|
|
636
|
+
background: canDelete ? "white" : "rgba(0,0,0,0.04)",
|
|
637
|
+
borderRadius: 10,
|
|
638
|
+
padding: "8px 10px",
|
|
639
|
+
cursor: canDelete ? "pointer" : "not-allowed",
|
|
640
|
+
display: "flex",
|
|
641
|
+
alignItems: "center",
|
|
642
|
+
gap: 8
|
|
643
|
+
},
|
|
644
|
+
children: [
|
|
645
|
+
deleting ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SmallSpinner, {}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TrashIcon, {}),
|
|
646
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 13 }, children: deleting ? "Deleting\u2026" : "Delete" })
|
|
647
|
+
]
|
|
648
|
+
}
|
|
649
|
+
) : null
|
|
650
|
+
]
|
|
651
|
+
}
|
|
652
|
+
);
|
|
653
|
+
};
|
|
654
|
+
function formatBytes(bytes) {
|
|
655
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "";
|
|
656
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
657
|
+
let v = bytes;
|
|
658
|
+
let i = 0;
|
|
659
|
+
while (v >= 1024 && i < units.length - 1) {
|
|
660
|
+
v /= 1024;
|
|
661
|
+
i++;
|
|
662
|
+
}
|
|
663
|
+
return `${v.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
|
|
664
|
+
}
|
|
665
|
+
function SmallSpinner() {
|
|
666
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
667
|
+
"span",
|
|
668
|
+
{
|
|
669
|
+
"aria-hidden": true,
|
|
670
|
+
style: {
|
|
671
|
+
width: 14,
|
|
672
|
+
height: 14,
|
|
673
|
+
borderRadius: "50%",
|
|
674
|
+
border: "2px solid rgba(0,0,0,0.2)",
|
|
675
|
+
borderTopColor: "rgba(0,0,0,0.7)",
|
|
676
|
+
display: "inline-block",
|
|
677
|
+
animation: "fileIconSpin 0.8s linear infinite"
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
function DefaultFileIcon() {
|
|
683
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
685
|
+
"path",
|
|
686
|
+
{
|
|
687
|
+
d: "M7 3h7l3 3v15a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Z",
|
|
688
|
+
stroke: "currentColor",
|
|
689
|
+
strokeWidth: "2"
|
|
690
|
+
}
|
|
691
|
+
),
|
|
692
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 3v4a1 1 0 0 0 1 1h4", stroke: "currentColor", strokeWidth: "2" })
|
|
693
|
+
] });
|
|
694
|
+
}
|
|
695
|
+
function TrashIcon() {
|
|
696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
|
|
697
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 6h18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
698
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
699
|
+
"path",
|
|
700
|
+
{
|
|
701
|
+
d: "M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2",
|
|
702
|
+
stroke: "currentColor",
|
|
703
|
+
strokeWidth: "2"
|
|
704
|
+
}
|
|
705
|
+
),
|
|
706
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
707
|
+
"path",
|
|
708
|
+
{
|
|
709
|
+
d: "M6 6l1 16a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-16",
|
|
710
|
+
stroke: "currentColor",
|
|
711
|
+
strokeWidth: "2"
|
|
712
|
+
}
|
|
713
|
+
),
|
|
714
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
715
|
+
"path",
|
|
716
|
+
{
|
|
717
|
+
d: "M10 11v7M14 11v7",
|
|
718
|
+
stroke: "currentColor",
|
|
719
|
+
strokeWidth: "2",
|
|
720
|
+
strokeLinecap: "round"
|
|
721
|
+
}
|
|
722
|
+
)
|
|
723
|
+
] });
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// src/components/FileIconGrid.tsx
|
|
727
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
728
|
+
function FileIconGrid(props) {
|
|
729
|
+
const {
|
|
730
|
+
files,
|
|
731
|
+
deleteDisabled,
|
|
732
|
+
onDeleted,
|
|
733
|
+
selectedId,
|
|
734
|
+
onSelect,
|
|
735
|
+
icon,
|
|
736
|
+
iconHtml,
|
|
737
|
+
className
|
|
738
|
+
} = props;
|
|
739
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
740
|
+
"div",
|
|
741
|
+
{
|
|
742
|
+
className,
|
|
743
|
+
style: {
|
|
744
|
+
display: "grid",
|
|
745
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
|
|
746
|
+
gap: 12
|
|
747
|
+
},
|
|
748
|
+
children: files.map((f) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
749
|
+
FileIconCard,
|
|
750
|
+
{
|
|
751
|
+
file: f,
|
|
752
|
+
deleteDisabled,
|
|
753
|
+
onDeleteFile: onDeleted,
|
|
754
|
+
onSelect,
|
|
755
|
+
selected: selectedId === f.Id,
|
|
756
|
+
icon,
|
|
757
|
+
iconHtml
|
|
758
|
+
},
|
|
759
|
+
f.Id
|
|
760
|
+
))
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/components/ContainerIdGridPanel.tsx
|
|
766
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
767
|
+
function ContainerIdGridPanel(props) {
|
|
768
|
+
const {
|
|
769
|
+
containerApiBaseUrl,
|
|
770
|
+
storageApiBaseUrl,
|
|
771
|
+
containerIds,
|
|
772
|
+
onContainerIdsChange,
|
|
773
|
+
accept = "*/*",
|
|
774
|
+
multiple = true,
|
|
775
|
+
selectedId,
|
|
776
|
+
onSelect,
|
|
777
|
+
icon,
|
|
778
|
+
iconHtml,
|
|
779
|
+
deleteDisabled,
|
|
780
|
+
className,
|
|
781
|
+
onDeleted
|
|
782
|
+
} = props;
|
|
783
|
+
const sdkDb = (0, import_react5.useMemo)(
|
|
784
|
+
() => new SparkStudioStorageSDK(containerApiBaseUrl),
|
|
785
|
+
[containerApiBaseUrl]
|
|
786
|
+
);
|
|
787
|
+
const sdkS3 = (0, import_react5.useMemo)(
|
|
788
|
+
() => new SparkStudioStorageSDK(storageApiBaseUrl),
|
|
789
|
+
[storageApiBaseUrl]
|
|
790
|
+
);
|
|
791
|
+
const [loading, setLoading] = (0, import_react5.useState)(false);
|
|
792
|
+
const [files, setFiles] = (0, import_react5.useState)([]);
|
|
793
|
+
const [dragOver, setDragOver] = (0, import_react5.useState)(false);
|
|
794
|
+
const [errMsg, setErrMsg] = (0, import_react5.useState)(null);
|
|
795
|
+
const idsRef = (0, import_react5.useRef)(containerIds);
|
|
796
|
+
(0, import_react5.useEffect)(() => {
|
|
797
|
+
idsRef.current = containerIds;
|
|
798
|
+
}, [containerIds]);
|
|
799
|
+
const createdByFileRef = (0, import_react5.useRef)(/* @__PURE__ */ new WeakMap());
|
|
800
|
+
(0, import_react5.useEffect)(() => {
|
|
801
|
+
let cancelled = false;
|
|
802
|
+
async function loadByIds(ids) {
|
|
803
|
+
setErrMsg(null);
|
|
804
|
+
if (!ids || ids.length === 0) {
|
|
805
|
+
setFiles([]);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
setLoading(true);
|
|
809
|
+
try {
|
|
810
|
+
const results = await Promise.all(
|
|
811
|
+
ids.map(async (id) => {
|
|
812
|
+
try {
|
|
813
|
+
const dto = await sdkDb.container.Read?.(id);
|
|
814
|
+
return dto ?? null;
|
|
815
|
+
} catch {
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
})
|
|
819
|
+
);
|
|
820
|
+
if (cancelled) return;
|
|
821
|
+
const map = /* @__PURE__ */ new Map();
|
|
822
|
+
for (const r of results) if (r?.Id) map.set(r.Id, r);
|
|
823
|
+
setFiles(
|
|
824
|
+
ids.map((id) => map.get(id)).filter(Boolean)
|
|
825
|
+
);
|
|
826
|
+
} catch (e) {
|
|
827
|
+
if (!cancelled)
|
|
828
|
+
setErrMsg(e instanceof Error ? e.message : "Failed to load files");
|
|
829
|
+
} finally {
|
|
830
|
+
if (!cancelled) setLoading(false);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
void loadByIds(containerIds);
|
|
834
|
+
return () => {
|
|
835
|
+
cancelled = true;
|
|
836
|
+
};
|
|
837
|
+
}, [containerIds, sdkDb]);
|
|
838
|
+
const getPresignedUrl = async (file) => {
|
|
839
|
+
const contentType = file.type || "application/octet-stream";
|
|
840
|
+
const containerDTO = await sdkDb.container.CreateFileContainer(
|
|
841
|
+
file.name,
|
|
842
|
+
file.size,
|
|
843
|
+
encodeURIComponent(contentType)
|
|
844
|
+
);
|
|
845
|
+
createdByFileRef.current.set(file, containerDTO);
|
|
846
|
+
let lastError;
|
|
847
|
+
for (let i = 1; i <= 3; i++) {
|
|
848
|
+
try {
|
|
849
|
+
return await sdkS3.s3.GetPreSignedUrl(containerDTO);
|
|
850
|
+
} catch (e) {
|
|
851
|
+
lastError = e;
|
|
852
|
+
if (i < 3) await new Promise((r) => setTimeout(r, 500 * i));
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
throw lastError instanceof Error ? lastError : new Error("Failed to fetch presigned URL");
|
|
856
|
+
};
|
|
857
|
+
const { uploads, startUploadsIfNeeded } = UseUploadManager({
|
|
858
|
+
autoUpload: true,
|
|
859
|
+
getPresignedUrl,
|
|
860
|
+
onUploadComplete: async (file) => {
|
|
861
|
+
setErrMsg(null);
|
|
862
|
+
const created = createdByFileRef.current.get(file);
|
|
863
|
+
if (created?.Id) {
|
|
864
|
+
const prev = idsRef.current ?? [];
|
|
865
|
+
const next = prev.includes(created.Id) ? prev : [...prev, created.Id];
|
|
866
|
+
onContainerIdsChange(next);
|
|
867
|
+
}
|
|
868
|
+
if (created?.Id) {
|
|
869
|
+
try {
|
|
870
|
+
const refreshed = await sdkDb.container.Read?.(created.Id);
|
|
871
|
+
if (refreshed) {
|
|
872
|
+
setFiles((prevFiles) => {
|
|
873
|
+
const nextFiles = prevFiles.slice();
|
|
874
|
+
const idx = nextFiles.findIndex((x) => x.Id === created.Id);
|
|
875
|
+
if (idx >= 0) nextFiles[idx] = refreshed;
|
|
876
|
+
else nextFiles.push(refreshed);
|
|
877
|
+
return nextFiles;
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
} catch {
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
},
|
|
884
|
+
onUploadError: (_file, error) => {
|
|
885
|
+
setErrMsg(error?.message ?? "Upload failed");
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
const handleDelete = async (file) => {
|
|
889
|
+
if (deleteDisabled) return;
|
|
890
|
+
await sdkDb.container.DeleteContainer(file.Id);
|
|
891
|
+
await sdkS3.s3.DeleteS3(file);
|
|
892
|
+
const prev = idsRef.current ?? [];
|
|
893
|
+
onContainerIdsChange(prev.filter((id) => id !== file.Id));
|
|
894
|
+
setFiles((prevFiles) => prevFiles.filter((x) => x.Id !== file.Id));
|
|
895
|
+
onDeleted?.(file);
|
|
896
|
+
};
|
|
897
|
+
const openPicker = () => {
|
|
898
|
+
const input = document.createElement("input");
|
|
899
|
+
input.type = "file";
|
|
900
|
+
input.multiple = multiple;
|
|
901
|
+
input.accept = accept;
|
|
902
|
+
input.onchange = () => {
|
|
903
|
+
if (input.files) startUploadsIfNeeded(input.files);
|
|
904
|
+
};
|
|
905
|
+
input.click();
|
|
906
|
+
};
|
|
907
|
+
const onDrop = (ev) => {
|
|
908
|
+
ev.preventDefault();
|
|
909
|
+
ev.stopPropagation();
|
|
910
|
+
setDragOver(false);
|
|
911
|
+
const list = ev.dataTransfer.files;
|
|
912
|
+
if (!list || list.length === 0) return;
|
|
913
|
+
startUploadsIfNeeded(list);
|
|
914
|
+
};
|
|
915
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, style: { display: "grid", gap: 12 }, children: [
|
|
916
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
917
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UploadProgressList, { uploads }),
|
|
918
|
+
errMsg ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 12, color: "crimson", marginTop: 6 }, children: errMsg }) : null
|
|
919
|
+
] }),
|
|
920
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
921
|
+
"div",
|
|
922
|
+
{
|
|
923
|
+
onDragEnter: (e) => {
|
|
924
|
+
e.preventDefault();
|
|
925
|
+
e.stopPropagation();
|
|
926
|
+
setDragOver(true);
|
|
927
|
+
},
|
|
928
|
+
onDragOver: (e) => {
|
|
929
|
+
e.preventDefault();
|
|
930
|
+
e.stopPropagation();
|
|
931
|
+
setDragOver(true);
|
|
932
|
+
},
|
|
933
|
+
onDragLeave: (e) => {
|
|
934
|
+
e.preventDefault();
|
|
935
|
+
e.stopPropagation();
|
|
936
|
+
setDragOver(false);
|
|
937
|
+
},
|
|
938
|
+
onDrop,
|
|
939
|
+
style: {
|
|
940
|
+
position: "relative",
|
|
941
|
+
borderRadius: 14,
|
|
942
|
+
border: `2px dashed ${dragOver ? "rgba(13,110,253,0.9)" : "rgba(0,0,0,0.15)"}`,
|
|
943
|
+
background: dragOver ? "rgba(13,110,253,0.06)" : "transparent",
|
|
944
|
+
padding: 12
|
|
945
|
+
},
|
|
946
|
+
children: [
|
|
947
|
+
dragOver ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
948
|
+
"div",
|
|
949
|
+
{
|
|
950
|
+
style: {
|
|
951
|
+
position: "absolute",
|
|
952
|
+
inset: 0,
|
|
953
|
+
borderRadius: 14,
|
|
954
|
+
display: "grid",
|
|
955
|
+
placeItems: "center",
|
|
956
|
+
pointerEvents: "none",
|
|
957
|
+
background: "rgba(13,110,253,0.10)",
|
|
958
|
+
fontWeight: 800
|
|
959
|
+
},
|
|
960
|
+
children: "Drop files to upload"
|
|
961
|
+
}
|
|
962
|
+
) : null,
|
|
963
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
964
|
+
"div",
|
|
965
|
+
{
|
|
966
|
+
style: {
|
|
967
|
+
display: "flex",
|
|
968
|
+
justifyContent: "space-between",
|
|
969
|
+
marginBottom: 10
|
|
970
|
+
},
|
|
971
|
+
children: [
|
|
972
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 700 }, children: "Files" }),
|
|
973
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
974
|
+
"button",
|
|
975
|
+
{
|
|
976
|
+
type: "button",
|
|
977
|
+
onClick: openPicker,
|
|
978
|
+
style: {
|
|
979
|
+
border: "1px solid rgba(0,0,0,0.18)",
|
|
980
|
+
background: "white",
|
|
981
|
+
borderRadius: 10,
|
|
982
|
+
padding: "8px 10px",
|
|
983
|
+
cursor: "pointer",
|
|
984
|
+
fontWeight: 600
|
|
985
|
+
},
|
|
986
|
+
children: "Browse\u2026"
|
|
987
|
+
}
|
|
988
|
+
)
|
|
989
|
+
]
|
|
990
|
+
}
|
|
991
|
+
),
|
|
992
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
993
|
+
"div",
|
|
994
|
+
{
|
|
995
|
+
className: "d-flex justify-content-center align-items-center",
|
|
996
|
+
style: { minHeight: 120 },
|
|
997
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "spinner-border text-secondary", role: "status" })
|
|
998
|
+
}
|
|
999
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1000
|
+
FileIconGrid,
|
|
1001
|
+
{
|
|
1002
|
+
files,
|
|
1003
|
+
deleteDisabled,
|
|
1004
|
+
onDeleted: handleDelete,
|
|
1005
|
+
selectedId,
|
|
1006
|
+
onSelect,
|
|
1007
|
+
icon,
|
|
1008
|
+
iconHtml
|
|
1009
|
+
}
|
|
1010
|
+
)
|
|
1011
|
+
]
|
|
1012
|
+
}
|
|
1013
|
+
)
|
|
1014
|
+
] });
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// src/components/ContainerUploadPanel.tsx
|
|
1018
|
+
var import_react10 = require("react");
|
|
1019
|
+
|
|
1020
|
+
// src/components/UploadContainer.tsx
|
|
1021
|
+
var import_react8 = require("react");
|
|
1022
|
+
|
|
1023
|
+
// src/components/UploadDropzone.tsx
|
|
1024
|
+
var import_react6 = require("react");
|
|
1025
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1026
|
+
var UploadDropzone = ({
|
|
1027
|
+
isDragging,
|
|
1028
|
+
onDragOver,
|
|
1029
|
+
onDragLeave,
|
|
1030
|
+
onDrop,
|
|
1031
|
+
className = "",
|
|
1032
|
+
style,
|
|
1033
|
+
children
|
|
1034
|
+
}) => {
|
|
1035
|
+
const baseClass = "rounded-3 d-flex flex-column align-items-center justify-content-center";
|
|
1036
|
+
const stateClass = isDragging ? "bg-body-secondary border-dashed border-2 border-secondary" : "bg-body-trasparent border-solid border-transparent border-2";
|
|
1037
|
+
const combinedClassName = `${baseClass} ${stateClass} ${className}`.trim();
|
|
1038
|
+
const handleDragOver = (e) => {
|
|
1039
|
+
e.preventDefault();
|
|
1040
|
+
onDragOver?.(e);
|
|
1041
|
+
};
|
|
1042
|
+
const handleDragLeave = (e) => {
|
|
1043
|
+
e.preventDefault();
|
|
1044
|
+
onDragLeave?.(e);
|
|
1045
|
+
};
|
|
1046
|
+
const handleDrop = (e) => {
|
|
1047
|
+
e.preventDefault();
|
|
1048
|
+
onDrop?.(e);
|
|
1049
|
+
};
|
|
1050
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1051
|
+
"div",
|
|
1052
|
+
{
|
|
1053
|
+
className: combinedClassName,
|
|
1054
|
+
style: { minHeight: "140px", ...style },
|
|
1055
|
+
onDragOver: handleDragOver,
|
|
1056
|
+
onDragLeave: handleDragLeave,
|
|
1057
|
+
onDrop: handleDrop,
|
|
1058
|
+
children
|
|
1059
|
+
}
|
|
1060
|
+
);
|
|
1061
|
+
};
|
|
1062
|
+
|
|
555
1063
|
// src/components/DesktopFileIcon.tsx
|
|
556
1064
|
var import_free_solid_svg_icons = require("@fortawesome/free-solid-svg-icons");
|
|
557
1065
|
var import_react_fontawesome = require("@fortawesome/react-fontawesome");
|
|
558
|
-
var
|
|
559
|
-
var
|
|
1066
|
+
var import_react7 = require("react");
|
|
1067
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
560
1068
|
function getFileExtension(name) {
|
|
561
1069
|
if (!name) return null;
|
|
562
1070
|
const lastDot = name.lastIndexOf(".");
|
|
@@ -635,13 +1143,13 @@ var DesktopFileIcon = ({
|
|
|
635
1143
|
onOpen,
|
|
636
1144
|
onDelete
|
|
637
1145
|
}) => {
|
|
638
|
-
const [contextMenuPos, setContextMenuPos] = (0,
|
|
1146
|
+
const [contextMenuPos, setContextMenuPos] = (0, import_react7.useState)(
|
|
639
1147
|
null
|
|
640
1148
|
);
|
|
641
|
-
const [isHovered, setIsHovered] = (0,
|
|
642
|
-
const [isDeleting, setIsDeleting] = (0,
|
|
643
|
-
const iconRef = (0,
|
|
644
|
-
const menuRef = (0,
|
|
1149
|
+
const [isHovered, setIsHovered] = (0, import_react7.useState)(false);
|
|
1150
|
+
const [isDeleting, setIsDeleting] = (0, import_react7.useState)(false);
|
|
1151
|
+
const iconRef = (0, import_react7.useRef)(null);
|
|
1152
|
+
const menuRef = (0, import_react7.useRef)(null);
|
|
645
1153
|
const handleDoubleClick = () => {
|
|
646
1154
|
if (isDeleting) return;
|
|
647
1155
|
if (onOpen) {
|
|
@@ -699,7 +1207,7 @@ var DesktopFileIcon = ({
|
|
|
699
1207
|
const formattedSize = typeof sizeBytes === "number" ? `${(sizeBytes / 1024).toFixed(1)} KB` : void 0;
|
|
700
1208
|
const ext = getFileExtension(name);
|
|
701
1209
|
const iconToRender = getIconForExtension(ext);
|
|
702
|
-
(0,
|
|
1210
|
+
(0, import_react7.useEffect)(() => {
|
|
703
1211
|
if (!contextMenuPos) return;
|
|
704
1212
|
const handleGlobalClick = (e) => {
|
|
705
1213
|
const target = e.target;
|
|
@@ -716,8 +1224,8 @@ var DesktopFileIcon = ({
|
|
|
716
1224
|
document.removeEventListener("mousedown", handleGlobalClick);
|
|
717
1225
|
};
|
|
718
1226
|
}, [contextMenuPos]);
|
|
719
|
-
return /* @__PURE__ */ (0,
|
|
720
|
-
/* @__PURE__ */ (0,
|
|
1227
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1228
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
721
1229
|
"div",
|
|
722
1230
|
{
|
|
723
1231
|
ref: iconRef,
|
|
@@ -738,7 +1246,7 @@ var DesktopFileIcon = ({
|
|
|
738
1246
|
onMouseEnter: () => setIsHovered(true),
|
|
739
1247
|
onMouseLeave: () => setIsHovered(false),
|
|
740
1248
|
children: [
|
|
741
|
-
/* @__PURE__ */ (0,
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
742
1250
|
"div",
|
|
743
1251
|
{
|
|
744
1252
|
className: "border rounded-3 d-flex align-items-center justify-content-center mb-1 shadow-sm position-relative",
|
|
@@ -747,17 +1255,17 @@ var DesktopFileIcon = ({
|
|
|
747
1255
|
height: 64
|
|
748
1256
|
},
|
|
749
1257
|
children: [
|
|
750
|
-
/* @__PURE__ */ (0,
|
|
751
|
-
isDeleting && /* @__PURE__ */ (0,
|
|
1258
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_fontawesome.FontAwesomeIcon, { icon: iconToRender, className: "fs-2" }),
|
|
1259
|
+
isDeleting && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "position-absolute top-50 start-50 translate-middle", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "spinner-border spinner-border-sm text-danger" }) })
|
|
752
1260
|
]
|
|
753
1261
|
}
|
|
754
1262
|
),
|
|
755
|
-
/* @__PURE__ */ (0,
|
|
756
|
-
formattedSize && /* @__PURE__ */ (0,
|
|
1263
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "small text-center text-truncate", style: { width: "100%" }, children: name }),
|
|
1264
|
+
formattedSize && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("small", { className: "text-muted mt-1", children: formattedSize })
|
|
757
1265
|
]
|
|
758
1266
|
}
|
|
759
1267
|
),
|
|
760
|
-
contextMenuPos && !isDeleting && /* @__PURE__ */ (0,
|
|
1268
|
+
contextMenuPos && !isDeleting && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
761
1269
|
"div",
|
|
762
1270
|
{
|
|
763
1271
|
ref: menuRef,
|
|
@@ -770,7 +1278,7 @@ var DesktopFileIcon = ({
|
|
|
770
1278
|
},
|
|
771
1279
|
onClick: (e) => e.stopPropagation(),
|
|
772
1280
|
children: [
|
|
773
|
-
/* @__PURE__ */ (0,
|
|
1281
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
774
1282
|
"button",
|
|
775
1283
|
{
|
|
776
1284
|
type: "button",
|
|
@@ -780,7 +1288,7 @@ var DesktopFileIcon = ({
|
|
|
780
1288
|
children: "Download file"
|
|
781
1289
|
}
|
|
782
1290
|
),
|
|
783
|
-
/* @__PURE__ */ (0,
|
|
1291
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
784
1292
|
"button",
|
|
785
1293
|
{
|
|
786
1294
|
type: "button",
|
|
@@ -790,8 +1298,8 @@ var DesktopFileIcon = ({
|
|
|
790
1298
|
children: "Copy download URL"
|
|
791
1299
|
}
|
|
792
1300
|
),
|
|
793
|
-
/* @__PURE__ */ (0,
|
|
794
|
-
/* @__PURE__ */ (0,
|
|
1301
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dropdown-divider" }),
|
|
1302
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
795
1303
|
"button",
|
|
796
1304
|
{
|
|
797
1305
|
type: "button",
|
|
@@ -807,160 +1315,165 @@ var DesktopFileIcon = ({
|
|
|
807
1315
|
};
|
|
808
1316
|
|
|
809
1317
|
// src/components/UploadContainer.tsx
|
|
810
|
-
var
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1318
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1319
|
+
function filesToFileList(files) {
|
|
1320
|
+
const dt = new DataTransfer();
|
|
1321
|
+
for (const f of files) dt.items.add(f);
|
|
1322
|
+
return dt.files;
|
|
1323
|
+
}
|
|
1324
|
+
var UploadContainer = (0, import_react8.forwardRef)(
|
|
1325
|
+
({
|
|
1326
|
+
multiple = true,
|
|
1327
|
+
accept = "*/*",
|
|
1328
|
+
onFilesSelected,
|
|
1329
|
+
existingFiles = [],
|
|
1330
|
+
existingFilesLoading = false,
|
|
1331
|
+
onExistingFileClick,
|
|
1332
|
+
onDeleteFile,
|
|
1333
|
+
autoUpload = false,
|
|
825
1334
|
getPresignedUrl,
|
|
826
1335
|
onUploadComplete,
|
|
827
1336
|
onUploadError
|
|
828
|
-
})
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1337
|
+
}, ref) => {
|
|
1338
|
+
const [isDragging, setIsDragging] = (0, import_react8.useState)(false);
|
|
1339
|
+
const inputId = (0, import_react8.useMemo)(() => `filePicker_${crypto.randomUUID()}`, []);
|
|
1340
|
+
const inputRef = (0, import_react8.useRef)(null);
|
|
1341
|
+
const { uploads, startUploadsIfNeeded } = UseUploadManager({
|
|
1342
|
+
autoUpload,
|
|
1343
|
+
getPresignedUrl,
|
|
1344
|
+
onUploadComplete,
|
|
1345
|
+
onUploadError
|
|
1346
|
+
});
|
|
1347
|
+
const selectAndUpload = (files) => {
|
|
1348
|
+
if (!files || files.length === 0) return;
|
|
1349
|
+
onFilesSelected?.(files);
|
|
1350
|
+
startUploadsIfNeeded(files);
|
|
1351
|
+
};
|
|
1352
|
+
(0, import_react8.useImperativeHandle)(ref, () => ({
|
|
1353
|
+
enqueueFiles(files) {
|
|
1354
|
+
const list = Array.isArray(files) ? filesToFileList(files) : files;
|
|
1355
|
+
selectAndUpload(list);
|
|
1356
|
+
},
|
|
1357
|
+
openFilePicker() {
|
|
1358
|
+
inputRef.current?.click();
|
|
1359
|
+
}
|
|
1360
|
+
}));
|
|
1361
|
+
const handleDragOver = (e) => {
|
|
1362
|
+
e.preventDefault();
|
|
1363
|
+
setIsDragging(true);
|
|
1364
|
+
};
|
|
1365
|
+
const handleDragLeave = (e) => {
|
|
1366
|
+
e.preventDefault();
|
|
1367
|
+
setIsDragging(false);
|
|
1368
|
+
};
|
|
1369
|
+
const handleDrop = (e) => {
|
|
1370
|
+
e.preventDefault();
|
|
1371
|
+
setIsDragging(false);
|
|
1372
|
+
const files = e.dataTransfer.files;
|
|
1373
|
+
if (!files || files.length === 0) return;
|
|
1374
|
+
selectAndUpload(files);
|
|
1375
|
+
};
|
|
1376
|
+
const handleExistingFileOpen = (file) => {
|
|
1377
|
+
if (onExistingFileClick) {
|
|
1378
|
+
onExistingFileClick(file);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
const a = document.createElement("a");
|
|
1382
|
+
a.href = file.PublicUrl ?? "";
|
|
1383
|
+
a.download = file.Name ?? "";
|
|
1384
|
+
a.target = "_blank";
|
|
1385
|
+
a.rel = "noopener noreferrer";
|
|
1386
|
+
document.body.appendChild(a);
|
|
1387
|
+
a.click();
|
|
1388
|
+
document.body.removeChild(a);
|
|
1389
|
+
};
|
|
1390
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1391
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "w-100", children: [
|
|
1392
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1393
|
+
"input",
|
|
1394
|
+
{
|
|
1395
|
+
ref: inputRef,
|
|
1396
|
+
id: inputId,
|
|
1397
|
+
type: "file",
|
|
1398
|
+
multiple,
|
|
1399
|
+
accept,
|
|
1400
|
+
className: "d-none",
|
|
1401
|
+
onChange: (e) => {
|
|
1402
|
+
if (!e.target.files) return;
|
|
1403
|
+
selectAndUpload(e.target.files);
|
|
1404
|
+
e.currentTarget.value = "";
|
|
1405
|
+
}
|
|
872
1406
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1407
|
+
),
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-start", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1409
|
+
"button",
|
|
1410
|
+
{
|
|
1411
|
+
type: "button",
|
|
1412
|
+
className: "btn btn-primary float-start",
|
|
1413
|
+
onClick: () => inputRef.current?.click(),
|
|
1414
|
+
children: "Browse files\u2026"
|
|
1415
|
+
}
|
|
1416
|
+
) })
|
|
1417
|
+
] }),
|
|
1418
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1419
|
+
UploadDropzone,
|
|
877
1420
|
{
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
transition: "background 0.12s, border-color 0.12s"
|
|
1421
|
+
isDragging,
|
|
1422
|
+
onDragOver: handleDragOver,
|
|
1423
|
+
onDragLeave: handleDragLeave,
|
|
1424
|
+
onDrop: handleDrop,
|
|
1425
|
+
className: "w-100",
|
|
1426
|
+
style: { minHeight: "100px", alignItems: "stretch" },
|
|
1427
|
+
children: [
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-100 mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "d-flex flex-column flex-md-row align-items-start align-items-md-center justify-content-between gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex-grow-1 w-100", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(UploadProgressList, { uploads }) }) }) }),
|
|
1429
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1430
|
+
"div",
|
|
1431
|
+
{
|
|
1432
|
+
className: "w-100 d-flex flex-wrap gap-4 align-content-start",
|
|
1433
|
+
style: { minHeight: "140px" },
|
|
1434
|
+
children: existingFilesLoading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-100 d-flex justify-content-center align-items-center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "spinner-border text-secondary", role: "status" }) }) : existingFiles.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1435
|
+
"div",
|
|
1436
|
+
{
|
|
1437
|
+
className: "w-100 d-flex flex-column align-items-center justify-content-center text-muted",
|
|
1438
|
+
style: {
|
|
1439
|
+
minHeight: "160px",
|
|
1440
|
+
padding: "20px",
|
|
1441
|
+
cursor: "pointer",
|
|
1442
|
+
transition: "background 0.12s, border-color 0.12s"
|
|
1443
|
+
},
|
|
1444
|
+
onClick: () => inputRef.current?.click(),
|
|
1445
|
+
children: [
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: "Drag & drop files here" }),
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("small", { className: "mt-1", children: "\u2026or click to browse" })
|
|
1448
|
+
]
|
|
1449
|
+
}
|
|
1450
|
+
) : existingFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1451
|
+
DesktopFileIcon,
|
|
1452
|
+
{
|
|
1453
|
+
name: file.Name,
|
|
1454
|
+
sizeBytes: file.FileSize,
|
|
1455
|
+
downloadUrl: file.PublicUrl,
|
|
1456
|
+
onOpen: () => handleExistingFileOpen(file),
|
|
1457
|
+
onDelete: () => onDeleteFile?.(file)
|
|
916
1458
|
},
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
onChange: (e) => {
|
|
929
|
-
if (!e.target.files) return;
|
|
930
|
-
onFilesSelected?.(e.target.files);
|
|
931
|
-
startUploadsIfNeeded(e.target.files);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
),
|
|
935
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "Drag & drop files here" }),
|
|
936
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "mt-1", children: "\u2026or click to browse" })
|
|
937
|
-
]
|
|
938
|
-
}
|
|
939
|
-
) : existingFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
940
|
-
DesktopFileIcon,
|
|
941
|
-
{
|
|
942
|
-
name: file.Name,
|
|
943
|
-
sizeBytes: file.FileSize,
|
|
944
|
-
downloadUrl: file.PublicUrl,
|
|
945
|
-
onOpen: () => handleExistingFileOpen(file),
|
|
946
|
-
onDelete: () => onDeleteFile?.(file)
|
|
947
|
-
},
|
|
948
|
-
file.Id
|
|
949
|
-
))
|
|
950
|
-
}
|
|
951
|
-
)
|
|
952
|
-
]
|
|
953
|
-
}
|
|
954
|
-
)
|
|
955
|
-
] });
|
|
956
|
-
};
|
|
1459
|
+
file.Id
|
|
1460
|
+
))
|
|
1461
|
+
}
|
|
1462
|
+
)
|
|
1463
|
+
]
|
|
1464
|
+
}
|
|
1465
|
+
)
|
|
1466
|
+
] });
|
|
1467
|
+
}
|
|
1468
|
+
);
|
|
1469
|
+
UploadContainer.displayName = "UploadContainer";
|
|
957
1470
|
|
|
958
1471
|
// src/hooks/UseContainers.ts
|
|
959
|
-
var
|
|
1472
|
+
var import_react9 = require("react");
|
|
960
1473
|
function UseContainers({ apiBaseUrl, parentId }) {
|
|
961
|
-
const [containers, setContainers] = (0,
|
|
962
|
-
const [loading, setLoading] = (0,
|
|
963
|
-
const [error, setError] = (0,
|
|
1474
|
+
const [containers, setContainers] = (0, import_react9.useState)([]);
|
|
1475
|
+
const [loading, setLoading] = (0, import_react9.useState)(false);
|
|
1476
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
964
1477
|
const load = async () => {
|
|
965
1478
|
setLoading(true);
|
|
966
1479
|
setError(null);
|
|
@@ -976,7 +1489,7 @@ function UseContainers({ apiBaseUrl, parentId }) {
|
|
|
976
1489
|
setLoading(false);
|
|
977
1490
|
}
|
|
978
1491
|
};
|
|
979
|
-
(0,
|
|
1492
|
+
(0, import_react9.useEffect)(() => {
|
|
980
1493
|
void load();
|
|
981
1494
|
}, [apiBaseUrl, parentId]);
|
|
982
1495
|
return {
|
|
@@ -989,11 +1502,12 @@ function UseContainers({ apiBaseUrl, parentId }) {
|
|
|
989
1502
|
}
|
|
990
1503
|
|
|
991
1504
|
// src/components/ContainerUploadPanel.tsx
|
|
992
|
-
var
|
|
1505
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
993
1506
|
var ContainerUploadPanel = ({
|
|
994
1507
|
containerApiBaseUrl,
|
|
995
1508
|
storageApiBaseUrl,
|
|
996
|
-
parentContainerId
|
|
1509
|
+
parentContainerId,
|
|
1510
|
+
uploadRef
|
|
997
1511
|
}) => {
|
|
998
1512
|
const { containers, setContainers, reload, loading } = UseContainers({
|
|
999
1513
|
apiBaseUrl: containerApiBaseUrl,
|
|
@@ -1048,9 +1562,10 @@ var ContainerUploadPanel = ({
|
|
|
1048
1562
|
await sdkS3.s3.DeleteS3(file);
|
|
1049
1563
|
setContainers((prev) => prev.filter((c) => c.Id !== file.Id));
|
|
1050
1564
|
};
|
|
1051
|
-
return /* @__PURE__ */ (0,
|
|
1565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1052
1566
|
UploadContainer,
|
|
1053
1567
|
{
|
|
1568
|
+
ref: uploadRef,
|
|
1054
1569
|
existingFiles: containers,
|
|
1055
1570
|
existingFilesLoading: loading,
|
|
1056
1571
|
onExistingFileClick: handleExistingFileClick,
|
|
@@ -1063,9 +1578,172 @@ var ContainerUploadPanel = ({
|
|
|
1063
1578
|
);
|
|
1064
1579
|
};
|
|
1065
1580
|
|
|
1581
|
+
// src/components/FileGridUploadPanel.tsx
|
|
1582
|
+
var import_react11 = require("react");
|
|
1583
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1584
|
+
function FileGridUploadPanel(props) {
|
|
1585
|
+
const {
|
|
1586
|
+
containerApiBaseUrl,
|
|
1587
|
+
storageApiBaseUrl,
|
|
1588
|
+
parentContainerId,
|
|
1589
|
+
accept = "*/*",
|
|
1590
|
+
multiple = true,
|
|
1591
|
+
selectedId,
|
|
1592
|
+
onSelect,
|
|
1593
|
+
icon,
|
|
1594
|
+
iconHtml,
|
|
1595
|
+
deleteDisabled
|
|
1596
|
+
} = props;
|
|
1597
|
+
const { containers, setContainers, reload, loading } = UseContainers({
|
|
1598
|
+
apiBaseUrl: containerApiBaseUrl,
|
|
1599
|
+
parentId: parentContainerId
|
|
1600
|
+
});
|
|
1601
|
+
const [isDragging, setIsDragging] = (0, import_react11.useState)(false);
|
|
1602
|
+
const [error, setError] = (0, import_react11.useState)(null);
|
|
1603
|
+
const sdkDb = (0, import_react11.useMemo)(() => new SparkStudioStorageSDK(containerApiBaseUrl), [containerApiBaseUrl]);
|
|
1604
|
+
const sdkS3 = (0, import_react11.useMemo)(() => new SparkStudioStorageSDK(storageApiBaseUrl), [storageApiBaseUrl]);
|
|
1605
|
+
const getPresignedUrl = async (file) => {
|
|
1606
|
+
const contentType = file.type || "application/octet-stream";
|
|
1607
|
+
const containerDTO = await sdkDb.container.CreateFileContainer(
|
|
1608
|
+
file.name,
|
|
1609
|
+
file.size,
|
|
1610
|
+
encodeURIComponent(contentType)
|
|
1611
|
+
);
|
|
1612
|
+
let lastError;
|
|
1613
|
+
for (let i = 1; i <= 3; i++) {
|
|
1614
|
+
try {
|
|
1615
|
+
return await sdkS3.s3.GetPreSignedUrl(containerDTO);
|
|
1616
|
+
} catch (e) {
|
|
1617
|
+
lastError = e;
|
|
1618
|
+
if (i < 3) await new Promise((r) => setTimeout(r, 500 * i));
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
throw lastError instanceof Error ? lastError : new Error("Failed to fetch presigned URL");
|
|
1622
|
+
};
|
|
1623
|
+
const { uploads, startUploadsIfNeeded } = UseUploadManager({
|
|
1624
|
+
autoUpload: true,
|
|
1625
|
+
getPresignedUrl,
|
|
1626
|
+
onUploadComplete: async () => {
|
|
1627
|
+
setError(null);
|
|
1628
|
+
await reload();
|
|
1629
|
+
},
|
|
1630
|
+
onUploadError: (_file, err) => {
|
|
1631
|
+
setError(err.message ?? "Upload failed");
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
const handleDeleteFile = async (file) => {
|
|
1635
|
+
const sdkDb2 = new SparkStudioStorageSDK(containerApiBaseUrl);
|
|
1636
|
+
const sdkS32 = new SparkStudioStorageSDK(storageApiBaseUrl);
|
|
1637
|
+
await sdkDb2.container.DeleteContainer(file.Id);
|
|
1638
|
+
await sdkS32.s3.DeleteS3(file);
|
|
1639
|
+
setContainers((prev) => prev.filter((c) => c.Id !== file.Id));
|
|
1640
|
+
};
|
|
1641
|
+
const openPicker = () => {
|
|
1642
|
+
const input = document.createElement("input");
|
|
1643
|
+
input.type = "file";
|
|
1644
|
+
input.multiple = multiple;
|
|
1645
|
+
input.accept = accept;
|
|
1646
|
+
input.onchange = () => {
|
|
1647
|
+
if (!input.files || input.files.length === 0) return;
|
|
1648
|
+
startUploadsIfNeeded(input.files);
|
|
1649
|
+
};
|
|
1650
|
+
input.click();
|
|
1651
|
+
};
|
|
1652
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "grid", gap: 12 }, children: [
|
|
1653
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
1654
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(UploadProgressList, { uploads }),
|
|
1655
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: 12, color: "crimson", marginTop: 6 }, children: error }) : null
|
|
1656
|
+
] }),
|
|
1657
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1658
|
+
"div",
|
|
1659
|
+
{
|
|
1660
|
+
onDragEnter: (e) => {
|
|
1661
|
+
e.preventDefault();
|
|
1662
|
+
e.stopPropagation();
|
|
1663
|
+
setIsDragging(true);
|
|
1664
|
+
},
|
|
1665
|
+
onDragOver: (e) => {
|
|
1666
|
+
e.preventDefault();
|
|
1667
|
+
e.stopPropagation();
|
|
1668
|
+
setIsDragging(true);
|
|
1669
|
+
},
|
|
1670
|
+
onDragLeave: (e) => {
|
|
1671
|
+
e.preventDefault();
|
|
1672
|
+
e.stopPropagation();
|
|
1673
|
+
setIsDragging(false);
|
|
1674
|
+
},
|
|
1675
|
+
onDrop: (e) => {
|
|
1676
|
+
e.preventDefault();
|
|
1677
|
+
e.stopPropagation();
|
|
1678
|
+
setIsDragging(false);
|
|
1679
|
+
const files = e.dataTransfer.files;
|
|
1680
|
+
if (!files || files.length === 0) return;
|
|
1681
|
+
startUploadsIfNeeded(files);
|
|
1682
|
+
},
|
|
1683
|
+
style: {
|
|
1684
|
+
position: "relative",
|
|
1685
|
+
borderRadius: 14,
|
|
1686
|
+
border: isDragging ? "2px dashed rgba(13,110,253,0.9)" : "2px dashed rgba(0,0,0,0.15)",
|
|
1687
|
+
background: isDragging ? "rgba(13,110,253,0.06)" : "transparent",
|
|
1688
|
+
padding: 12
|
|
1689
|
+
},
|
|
1690
|
+
children: [
|
|
1691
|
+
isDragging ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1692
|
+
"div",
|
|
1693
|
+
{
|
|
1694
|
+
style: {
|
|
1695
|
+
position: "absolute",
|
|
1696
|
+
inset: 0,
|
|
1697
|
+
borderRadius: 14,
|
|
1698
|
+
display: "grid",
|
|
1699
|
+
placeItems: "center",
|
|
1700
|
+
pointerEvents: "none",
|
|
1701
|
+
background: "rgba(13,110,253,0.08)",
|
|
1702
|
+
fontWeight: 800
|
|
1703
|
+
},
|
|
1704
|
+
children: "Drop files to upload"
|
|
1705
|
+
}
|
|
1706
|
+
) : null,
|
|
1707
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 10 }, children: [
|
|
1708
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontWeight: 700 }, children: "Files" }),
|
|
1709
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1710
|
+
"button",
|
|
1711
|
+
{
|
|
1712
|
+
type: "button",
|
|
1713
|
+
onClick: openPicker,
|
|
1714
|
+
style: {
|
|
1715
|
+
border: "1px solid rgba(0,0,0,0.18)",
|
|
1716
|
+
background: "white",
|
|
1717
|
+
borderRadius: 10,
|
|
1718
|
+
padding: "8px 10px",
|
|
1719
|
+
cursor: "pointer",
|
|
1720
|
+
fontWeight: 600
|
|
1721
|
+
},
|
|
1722
|
+
children: "Browse\u2026"
|
|
1723
|
+
}
|
|
1724
|
+
)
|
|
1725
|
+
] }),
|
|
1726
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "d-flex justify-content-center align-items-center", style: { minHeight: 120 }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "spinner-border text-secondary", role: "status" }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1727
|
+
FileIconGrid,
|
|
1728
|
+
{
|
|
1729
|
+
files: containers,
|
|
1730
|
+
deleteDisabled,
|
|
1731
|
+
onDeleted: handleDeleteFile,
|
|
1732
|
+
selectedId,
|
|
1733
|
+
onSelect,
|
|
1734
|
+
icon,
|
|
1735
|
+
iconHtml
|
|
1736
|
+
}
|
|
1737
|
+
)
|
|
1738
|
+
]
|
|
1739
|
+
}
|
|
1740
|
+
)
|
|
1741
|
+
] });
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1066
1744
|
// src/components/SingleFileProcessUploader.tsx
|
|
1067
|
-
var
|
|
1068
|
-
var
|
|
1745
|
+
var import_react12 = require("react");
|
|
1746
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1069
1747
|
var SingleFileProcessUploader = ({
|
|
1070
1748
|
getPresignedUrl,
|
|
1071
1749
|
onUploadComplete,
|
|
@@ -1074,28 +1752,28 @@ var SingleFileProcessUploader = ({
|
|
|
1074
1752
|
disabled,
|
|
1075
1753
|
uploadOnDrop = false
|
|
1076
1754
|
}) => {
|
|
1077
|
-
const [selectedFile, setSelectedFile] = (0,
|
|
1078
|
-
const [isDragging, setIsDragging] = (0,
|
|
1079
|
-
const [progress, setProgress] = (0,
|
|
1080
|
-
const [status, setStatus] = (0,
|
|
1081
|
-
const fileInputRef = (0,
|
|
1755
|
+
const [selectedFile, setSelectedFile] = (0, import_react12.useState)(null);
|
|
1756
|
+
const [isDragging, setIsDragging] = (0, import_react12.useState)(false);
|
|
1757
|
+
const [progress, setProgress] = (0, import_react12.useState)(null);
|
|
1758
|
+
const [status, setStatus] = (0, import_react12.useState)("idle");
|
|
1759
|
+
const fileInputRef = (0, import_react12.useRef)(null);
|
|
1082
1760
|
function getErrorMessage(err) {
|
|
1083
1761
|
if (err instanceof Error) return err.message;
|
|
1084
1762
|
if (typeof err === "string") return err;
|
|
1085
1763
|
return "Unknown error during upload.";
|
|
1086
1764
|
}
|
|
1087
1765
|
const isUploading = status === "uploading";
|
|
1088
|
-
const resetUiForNewFile = (0,
|
|
1766
|
+
const resetUiForNewFile = (0, import_react12.useCallback)((file) => {
|
|
1089
1767
|
setSelectedFile(file);
|
|
1090
1768
|
setStatus("idle");
|
|
1091
1769
|
setProgress(null);
|
|
1092
1770
|
}, []);
|
|
1093
|
-
const handleBrowseClick = (0,
|
|
1771
|
+
const handleBrowseClick = (0, import_react12.useCallback)(() => {
|
|
1094
1772
|
if (disabled) return;
|
|
1095
1773
|
if (isUploading) return;
|
|
1096
1774
|
fileInputRef.current?.click();
|
|
1097
1775
|
}, [disabled, isUploading]);
|
|
1098
|
-
const startUpload = (0,
|
|
1776
|
+
const startUpload = (0, import_react12.useCallback)(
|
|
1099
1777
|
async (file) => {
|
|
1100
1778
|
if (disabled) return;
|
|
1101
1779
|
if (isUploading) return;
|
|
@@ -1119,7 +1797,7 @@ var SingleFileProcessUploader = ({
|
|
|
1119
1797
|
},
|
|
1120
1798
|
[disabled, getPresignedUrl, isUploading, onUploadComplete, onUploadFailed]
|
|
1121
1799
|
);
|
|
1122
|
-
const handleFilesSelected = (0,
|
|
1800
|
+
const handleFilesSelected = (0, import_react12.useCallback)(
|
|
1123
1801
|
(files) => {
|
|
1124
1802
|
if (!files || files.length === 0) return;
|
|
1125
1803
|
if (disabled) return;
|
|
@@ -1164,8 +1842,8 @@ var SingleFileProcessUploader = ({
|
|
|
1164
1842
|
disabled ? "opacity-50" : "cursor-pointer",
|
|
1165
1843
|
isDragging ? "bg-body-secondary border-dashed border-2 border-secondary" : "bg-body-trasparent border-dashed border-2"
|
|
1166
1844
|
].join(" ");
|
|
1167
|
-
return /* @__PURE__ */ (0,
|
|
1168
|
-
isUploading ? /* @__PURE__ */ (0,
|
|
1845
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "d-flex flex-column gap-2", children: [
|
|
1846
|
+
isUploading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "small", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "progress", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1169
1847
|
"div",
|
|
1170
1848
|
{
|
|
1171
1849
|
className: "progress-bar progress-bar-striped progress-bar-animated",
|
|
@@ -1175,7 +1853,7 @@ var SingleFileProcessUploader = ({
|
|
|
1175
1853
|
"aria-valuenow": progress ?? 0,
|
|
1176
1854
|
style: { width: `${progress ?? 0}%` }
|
|
1177
1855
|
}
|
|
1178
|
-
) }) }) : /* @__PURE__ */ (0,
|
|
1856
|
+
) }) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1179
1857
|
"div",
|
|
1180
1858
|
{
|
|
1181
1859
|
className: dropzoneClasses,
|
|
@@ -1186,7 +1864,7 @@ var SingleFileProcessUploader = ({
|
|
|
1186
1864
|
role: "button",
|
|
1187
1865
|
"aria-disabled": disabled,
|
|
1188
1866
|
children: [
|
|
1189
|
-
/* @__PURE__ */ (0,
|
|
1867
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1190
1868
|
"input",
|
|
1191
1869
|
{
|
|
1192
1870
|
ref: fileInputRef,
|
|
@@ -1197,8 +1875,8 @@ var SingleFileProcessUploader = ({
|
|
|
1197
1875
|
disabled: disabled || isUploading
|
|
1198
1876
|
}
|
|
1199
1877
|
),
|
|
1200
|
-
selectedFile ? /* @__PURE__ */ (0,
|
|
1201
|
-
/* @__PURE__ */ (0,
|
|
1878
|
+
selectedFile ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "small", children: [
|
|
1879
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1202
1880
|
"button",
|
|
1203
1881
|
{
|
|
1204
1882
|
type: "button",
|
|
@@ -1207,15 +1885,15 @@ var SingleFileProcessUploader = ({
|
|
|
1207
1885
|
children: "Browse file\u2026"
|
|
1208
1886
|
}
|
|
1209
1887
|
),
|
|
1210
|
-
/* @__PURE__ */ (0,
|
|
1211
|
-
/* @__PURE__ */ (0,
|
|
1888
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: "Selected file:" }),
|
|
1212
1890
|
" ",
|
|
1213
1891
|
selectedFile.name
|
|
1214
1892
|
] }),
|
|
1215
|
-
/* @__PURE__ */ (0,
|
|
1216
|
-
uploadOnDrop && /* @__PURE__ */ (0,
|
|
1217
|
-
] }) : /* @__PURE__ */ (0,
|
|
1218
|
-
/* @__PURE__ */ (0,
|
|
1893
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-muted", children: "Click here to change file or drag a new one." }),
|
|
1894
|
+
uploadOnDrop && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-muted", children: "Upload starts automatically." })
|
|
1895
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "small", children: [
|
|
1896
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1219
1897
|
"button",
|
|
1220
1898
|
{
|
|
1221
1899
|
type: "button",
|
|
@@ -1224,13 +1902,13 @@ var SingleFileProcessUploader = ({
|
|
|
1224
1902
|
children: "Browse file\u2026"
|
|
1225
1903
|
}
|
|
1226
1904
|
),
|
|
1227
|
-
/* @__PURE__ */ (0,
|
|
1228
|
-
/* @__PURE__ */ (0,
|
|
1905
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { children: "Drag & drop a file here" }),
|
|
1906
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-muted", children: "or click to browse" })
|
|
1229
1907
|
] })
|
|
1230
1908
|
]
|
|
1231
1909
|
}
|
|
1232
1910
|
),
|
|
1233
|
-
!uploadOnDrop && /* @__PURE__ */ (0,
|
|
1911
|
+
!uploadOnDrop && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1234
1912
|
"button",
|
|
1235
1913
|
{
|
|
1236
1914
|
type: "button",
|
|
@@ -1244,34 +1922,44 @@ var SingleFileProcessUploader = ({
|
|
|
1244
1922
|
};
|
|
1245
1923
|
|
|
1246
1924
|
// src/views/HomeView.tsx
|
|
1925
|
+
var import_react13 = require("react");
|
|
1247
1926
|
var import_authentication_ui = require("@sparkstudio/authentication-ui");
|
|
1248
|
-
var
|
|
1927
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1928
|
+
var CONTAINER_API = "https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod";
|
|
1929
|
+
var STORAGE_API = "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod";
|
|
1249
1930
|
function HomeView() {
|
|
1250
|
-
return /* @__PURE__ */ (0,
|
|
1931
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1251
1932
|
import_authentication_ui.AuthenticatorProvider,
|
|
1252
1933
|
{
|
|
1253
1934
|
googleClientId: import_authentication_ui.AppSettings.GoogleClientId,
|
|
1254
1935
|
authenticationUrl: import_authentication_ui.AppSettings.AuthenticationUrl,
|
|
1255
1936
|
accountsUrl: import_authentication_ui.AppSettings.AccountsUrl,
|
|
1256
|
-
children: /* @__PURE__ */ (0,
|
|
1937
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(HomeContent, {})
|
|
1257
1938
|
}
|
|
1258
1939
|
);
|
|
1259
1940
|
}
|
|
1260
1941
|
function HomeContent() {
|
|
1261
1942
|
const { user } = (0, import_authentication_ui.useUser)();
|
|
1943
|
+
const [ids, setIds] = (0, import_react13.useState)([]);
|
|
1944
|
+
const [selectedId, setSelectedId] = (0, import_react13.useState)(void 0);
|
|
1945
|
+
const [selectedFile, setSelectedFile] = (0, import_react13.useState)(null);
|
|
1946
|
+
(0, import_react13.useEffect)(() => {
|
|
1947
|
+
if (selectedId && !ids.includes(selectedId)) {
|
|
1948
|
+
setSelectedId(void 0);
|
|
1949
|
+
setSelectedFile(null);
|
|
1950
|
+
}
|
|
1951
|
+
}, [ids, selectedId]);
|
|
1262
1952
|
async function getPresignedUrlFromApi(file) {
|
|
1263
|
-
const
|
|
1264
|
-
"https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
|
|
1265
|
-
//"https://localhost:5001"
|
|
1266
|
-
);
|
|
1953
|
+
const sdk = new SparkStudioStorageSDK(STORAGE_API);
|
|
1267
1954
|
const contentType = file.type || "application/octet-stream";
|
|
1268
|
-
|
|
1269
|
-
|
|
1955
|
+
return sdk.s3.GetTemporaryPreSignedUrl(
|
|
1956
|
+
new TemporaryFileDTO({ Name: file.name, ContentType: contentType })
|
|
1957
|
+
);
|
|
1270
1958
|
}
|
|
1271
|
-
return /* @__PURE__ */ (0,
|
|
1272
|
-
/* @__PURE__ */ (0,
|
|
1273
|
-
user
|
|
1274
|
-
/* @__PURE__ */ (0,
|
|
1959
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1960
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_authentication_ui.UserInfoCard, {}),
|
|
1961
|
+
user ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1962
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1275
1963
|
SingleFileProcessUploader,
|
|
1276
1964
|
{
|
|
1277
1965
|
uploadOnDrop: true,
|
|
@@ -1282,14 +1970,46 @@ function HomeContent() {
|
|
|
1282
1970
|
}
|
|
1283
1971
|
}
|
|
1284
1972
|
),
|
|
1285
|
-
/* @__PURE__ */ (0,
|
|
1286
|
-
|
|
1973
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1974
|
+
ContainerIdGridPanel,
|
|
1287
1975
|
{
|
|
1288
|
-
containerApiBaseUrl:
|
|
1289
|
-
storageApiBaseUrl:
|
|
1976
|
+
containerApiBaseUrl: CONTAINER_API,
|
|
1977
|
+
storageApiBaseUrl: STORAGE_API,
|
|
1978
|
+
containerIds: ids,
|
|
1979
|
+
onContainerIdsChange: setIds,
|
|
1980
|
+
multiple: true,
|
|
1981
|
+
accept: "*/*",
|
|
1982
|
+
selectedId,
|
|
1983
|
+
onSelect: (file) => {
|
|
1984
|
+
setSelectedId(file.Id);
|
|
1985
|
+
setSelectedFile(file);
|
|
1986
|
+
},
|
|
1987
|
+
onDeleted: (file) => {
|
|
1988
|
+
console.log("Deleted:", file);
|
|
1989
|
+
if (selectedId === file.Id) {
|
|
1990
|
+
setSelectedId(void 0);
|
|
1991
|
+
setSelectedFile(null);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1290
1994
|
}
|
|
1291
|
-
)
|
|
1292
|
-
|
|
1995
|
+
),
|
|
1996
|
+
selectedFile ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1997
|
+
"div",
|
|
1998
|
+
{
|
|
1999
|
+
style: {
|
|
2000
|
+
marginTop: 12,
|
|
2001
|
+
padding: 12,
|
|
2002
|
+
border: "1px solid rgba(0,0,0,0.12)",
|
|
2003
|
+
borderRadius: 12
|
|
2004
|
+
},
|
|
2005
|
+
children: [
|
|
2006
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontWeight: 700 }, children: "Selected file" }),
|
|
2007
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { children: selectedFile.Name ?? "(no name)" }),
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 12, opacity: 0.7 }, children: selectedFile.PublicUrl ?? "(no public url)" })
|
|
2009
|
+
]
|
|
2010
|
+
}
|
|
2011
|
+
) : null
|
|
2012
|
+
] }) : null
|
|
1293
2013
|
] });
|
|
1294
2014
|
}
|
|
1295
2015
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1297,9 +2017,13 @@ function HomeContent() {
|
|
|
1297
2017
|
AWSPresignedUrlDTO,
|
|
1298
2018
|
Container,
|
|
1299
2019
|
ContainerDTO,
|
|
2020
|
+
ContainerIdGridPanel,
|
|
1300
2021
|
ContainerType,
|
|
1301
2022
|
ContainerUploadPanel,
|
|
1302
2023
|
DesktopFileIcon,
|
|
2024
|
+
FileGridUploadPanel,
|
|
2025
|
+
FileIconCard,
|
|
2026
|
+
FileIconGrid,
|
|
1303
2027
|
Home,
|
|
1304
2028
|
HomeView,
|
|
1305
2029
|
S3,
|