@rebasepro/studio 0.0.1-canary.eae7889 → 0.1.0
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/{ApiExplorer-gMJt5JrS.js → ApiExplorer-DHVmWYfK.js} +13 -13
- package/dist/ApiExplorer-DHVmWYfK.js.map +1 -0
- package/dist/AuthSimulationSelector-CM488Eei.js +106 -0
- package/dist/AuthSimulationSelector-CM488Eei.js.map +1 -0
- package/dist/{JSEditor-D8nVp3Lp.js → JSEditor-CSHA0t_O.js} +2 -2
- package/dist/{JSEditor-D8nVp3Lp.js.map → JSEditor-CSHA0t_O.js.map} +1 -1
- package/dist/{RLSEditor-DBH09u9v.js → RLSEditor-BzDjqo6w.js} +46 -5
- package/dist/RLSEditor-BzDjqo6w.js.map +1 -0
- package/dist/{SQLEditor-CkVx9vgr.js → SQLEditor-Cr9Kg_Qg.js} +63 -75
- package/dist/SQLEditor-Cr9Kg_Qg.js.map +1 -0
- package/dist/{SchemaVisualizer-BgD5Zb77.js → SchemaVisualizer-BGpmzyXT.js} +5 -5
- package/dist/SchemaVisualizer-BGpmzyXT.js.map +1 -0
- package/dist/StorageView-BYoslzBR.js +870 -0
- package/dist/StorageView-BYoslzBR.js.map +1 -0
- package/dist/core/src/components/BootstrapAdminBanner.d.ts +4 -0
- package/dist/core/src/components/LoginView/LoginView.d.ts +22 -0
- package/dist/core/src/components/common/useDataTableController.d.ts +3 -3
- package/dist/core/src/components/index.d.ts +1 -0
- package/dist/core/src/hooks/data/useRelationSelector.d.ts +2 -2
- package/dist/core/src/hooks/index.d.ts +1 -0
- package/dist/core/src/hooks/useCollapsedGroups.d.ts +16 -1
- package/dist/core/src/hooks/useResolvedComponent.d.ts +47 -0
- package/dist/index.es.js +79 -71
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +821 -834
- package/dist/index.umd.js.map +1 -1
- package/dist/types/src/controllers/auth.d.ts +8 -2
- package/dist/types/src/controllers/client.d.ts +13 -0
- package/dist/types/src/controllers/collection_registry.d.ts +2 -1
- package/dist/types/src/controllers/data_driver.d.ts +36 -1
- package/dist/types/src/controllers/navigation.d.ts +18 -6
- package/dist/types/src/controllers/registry.d.ts +9 -1
- package/dist/types/src/controllers/side_entity_controller.d.ts +7 -0
- package/dist/types/src/rebase_context.d.ts +17 -0
- package/dist/types/src/types/backend_hooks.d.ts +187 -0
- package/dist/types/src/types/collections.d.ts +31 -11
- package/dist/types/src/types/component_ref.d.ts +47 -0
- package/dist/types/src/types/cron.d.ts +1 -1
- package/dist/types/src/types/entity_views.d.ts +6 -7
- package/dist/types/src/types/formex.d.ts +40 -0
- package/dist/types/src/types/index.d.ts +3 -0
- package/dist/types/src/types/plugins.d.ts +6 -3
- package/dist/types/src/types/properties.d.ts +72 -88
- package/dist/types/src/types/slots.d.ts +20 -10
- package/dist/types/src/types/translations.d.ts +6 -0
- package/dist/ui/src/components/FileUpload.d.ts +1 -1
- package/dist/ui/src/components/SearchBar.d.ts +5 -1
- package/dist/ui/src/styles.d.ts +2 -2
- package/package.json +10 -10
- package/src/components/ApiExplorer/ApiExplorer.tsx +7 -5
- package/src/components/ApiExplorer/TryItPanel.tsx +29 -53
- package/src/components/AuthSimulationSelector.tsx +13 -17
- package/src/components/RLSEditor/RLSEditor.tsx +82 -3
- package/src/components/SQLEditor/SQLEditor.tsx +16 -18
- package/src/components/SQLEditor/SchemaBrowser.tsx +6 -8
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +20 -22
- package/src/components/StorageView/StorageView.tsx +719 -304
- package/src/components/StudioHomePage.tsx +4 -1
- package/dist/ApiExplorer-gMJt5JrS.js.map +0 -1
- package/dist/AuthSimulationSelector-BF4rkRGp.js +0 -118
- package/dist/AuthSimulationSelector-BF4rkRGp.js.map +0 -1
- package/dist/RLSEditor-DBH09u9v.js.map +0 -1
- package/dist/SQLEditor-CkVx9vgr.js.map +0 -1
- package/dist/SchemaVisualizer-BgD5Zb77.js.map +0 -1
- package/dist/StorageView-CTqGFhY9.js +0 -907
- package/dist/StorageView-CTqGFhY9.js.map +0 -1
|
@@ -0,0 +1,870 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
|
|
3
|
+
import { Tooltip, IconButton, iconSize, Typography, Button, cls, Chip, defaultBorderMixin, Dialog, DialogContent, DialogActions, LoadingButton, TextField, CircularProgress, Checkbox, DialogTitle, FileUpload } from "@rebasepro/ui";
|
|
4
|
+
import { ArrowLeftIcon, Trash2Icon, XIcon, LayoutGridIcon, ListIcon, RefreshCwIcon, FolderPlusIcon, UploadCloudIcon, PlusIcon, FolderIcon, DownloadIcon, Music2Icon, CheckIcon, CopyIcon, FileTextIcon, ImageIcon, VideoIcon } from "lucide-react";
|
|
5
|
+
import { useStorageSource, useSnackbarController, useApiConfig, ErrorView } from "@rebasepro/core";
|
|
6
|
+
import { useSearchParams } from "react-router-dom";
|
|
7
|
+
import { useDropzone } from "react-dropzone";
|
|
8
|
+
function formatFileSize(bytes) {
|
|
9
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
10
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
11
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
12
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
13
|
+
}
|
|
14
|
+
function getFileIcon(contentType) {
|
|
15
|
+
if (!contentType) return FileTextIcon;
|
|
16
|
+
if (contentType.startsWith("image/")) return ImageIcon;
|
|
17
|
+
if (contentType.startsWith("video/")) return VideoIcon;
|
|
18
|
+
if (contentType.startsWith("audio/")) return Music2Icon;
|
|
19
|
+
return FileTextIcon;
|
|
20
|
+
}
|
|
21
|
+
function getExtension(name) {
|
|
22
|
+
const parts = name.split(".");
|
|
23
|
+
return parts.length > 1 ? parts[parts.length - 1].toUpperCase() : "";
|
|
24
|
+
}
|
|
25
|
+
function breadcrumbSegments(path) {
|
|
26
|
+
if (!path || path === "/") return [{
|
|
27
|
+
label: "Root",
|
|
28
|
+
path: ""
|
|
29
|
+
}];
|
|
30
|
+
const parts = path.split("/").filter(Boolean);
|
|
31
|
+
const segments = [{
|
|
32
|
+
label: "Root",
|
|
33
|
+
path: ""
|
|
34
|
+
}];
|
|
35
|
+
let accumulated = "";
|
|
36
|
+
for (const part of parts) {
|
|
37
|
+
accumulated = accumulated ? `${accumulated}/${part}` : part;
|
|
38
|
+
segments.push({
|
|
39
|
+
label: part,
|
|
40
|
+
path: accumulated
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return segments;
|
|
44
|
+
}
|
|
45
|
+
function UploadDialog({
|
|
46
|
+
open,
|
|
47
|
+
currentPath,
|
|
48
|
+
onClose,
|
|
49
|
+
onUpload
|
|
50
|
+
}) {
|
|
51
|
+
const [uploading, setUploading] = useState(false);
|
|
52
|
+
const [selectedFiles, setSelectedFiles] = useState([]);
|
|
53
|
+
const [error, setError] = useState(null);
|
|
54
|
+
const handleFilesAdded = useCallback((files) => {
|
|
55
|
+
setSelectedFiles((prev) => [...prev, ...files]);
|
|
56
|
+
}, []);
|
|
57
|
+
const handleRemoveFile = useCallback((index) => {
|
|
58
|
+
setSelectedFiles((prev_0) => prev_0.filter((_, i) => i !== index));
|
|
59
|
+
}, []);
|
|
60
|
+
const handleUpload = useCallback(async () => {
|
|
61
|
+
if (selectedFiles.length === 0) return;
|
|
62
|
+
setUploading(true);
|
|
63
|
+
setError(null);
|
|
64
|
+
try {
|
|
65
|
+
await onUpload(selectedFiles);
|
|
66
|
+
setSelectedFiles([]);
|
|
67
|
+
onClose();
|
|
68
|
+
} catch (err) {
|
|
69
|
+
setError(err instanceof Error ? err.message : "Upload failed");
|
|
70
|
+
} finally {
|
|
71
|
+
setUploading(false);
|
|
72
|
+
}
|
|
73
|
+
}, [selectedFiles, onUpload, onClose]);
|
|
74
|
+
const handleClose = useCallback(() => {
|
|
75
|
+
if (!uploading) {
|
|
76
|
+
setSelectedFiles([]);
|
|
77
|
+
setError(null);
|
|
78
|
+
onClose();
|
|
79
|
+
}
|
|
80
|
+
}, [uploading, onClose]);
|
|
81
|
+
return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: (o) => !o && handleClose(), maxWidth: "md", children: [
|
|
82
|
+
/* @__PURE__ */ jsxs(DialogTitle, { children: [
|
|
83
|
+
"Upload Files",
|
|
84
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-text-secondary dark:text-text-secondary-dark mt-0.5 block", children: [
|
|
85
|
+
"to ",
|
|
86
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-primary", children: [
|
|
87
|
+
"/",
|
|
88
|
+
currentPath || "root"
|
|
89
|
+
] })
|
|
90
|
+
] })
|
|
91
|
+
] }),
|
|
92
|
+
/* @__PURE__ */ jsxs(DialogContent, { className: "space-y-4", children: [
|
|
93
|
+
/* @__PURE__ */ jsx(FileUpload, { onFilesAdded: handleFilesAdded, size: "large", uploadDescription: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center pointer-events-none", children: [
|
|
94
|
+
/* @__PURE__ */ jsx(UploadCloudIcon, { className: "text-surface-accent-400 mb-2 w-8 h-8" }),
|
|
95
|
+
/* @__PURE__ */ jsx(Typography, { variant: "label", children: "Drop files here or click to browse" }),
|
|
96
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: "Any file type supported" })
|
|
97
|
+
] }) }),
|
|
98
|
+
error && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-red-500 block whitespace-pre-line", children: error }),
|
|
99
|
+
selectedFiles.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
100
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
|
|
101
|
+
"Selected files (",
|
|
102
|
+
selectedFiles.length,
|
|
103
|
+
")"
|
|
104
|
+
] }),
|
|
105
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-40 overflow-auto space-y-1", children: selectedFiles.map((file, index_0) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-2 rounded bg-surface-100 dark:bg-surface-800", children: [
|
|
106
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 mr-2", children: [
|
|
107
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "truncate", children: file.name }),
|
|
108
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: formatFileSize(file.size) })
|
|
109
|
+
] }),
|
|
110
|
+
/* @__PURE__ */ jsx(IconButton, { size: "small", onClick: (e) => {
|
|
111
|
+
e.stopPropagation();
|
|
112
|
+
handleRemoveFile(index_0);
|
|
113
|
+
}, disabled: uploading, children: /* @__PURE__ */ jsx(XIcon, { size: 14 }) })
|
|
114
|
+
] }, `${file.name}-${index_0}`)) })
|
|
115
|
+
] })
|
|
116
|
+
] }),
|
|
117
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [
|
|
118
|
+
/* @__PURE__ */ jsx(Button, { variant: "text", onClick: handleClose, disabled: uploading, children: "Cancel" }),
|
|
119
|
+
/* @__PURE__ */ jsx(Button, { variant: "filled", onClick: handleUpload, disabled: selectedFiles.length === 0 || uploading, startIcon: uploading ? /* @__PURE__ */ jsx(CircularProgress, { size: "smallest" }) : /* @__PURE__ */ jsx(UploadCloudIcon, { size: 14 }), children: uploading ? "Uploading..." : `Upload${selectedFiles.length > 0 ? ` (${selectedFiles.length})` : ""}` })
|
|
120
|
+
] })
|
|
121
|
+
] });
|
|
122
|
+
}
|
|
123
|
+
function FilePreviewPanel({
|
|
124
|
+
file,
|
|
125
|
+
onClose,
|
|
126
|
+
onDelete,
|
|
127
|
+
downloadUrl
|
|
128
|
+
}) {
|
|
129
|
+
file.contentType?.startsWith("image/");
|
|
130
|
+
file.contentType?.startsWith("video/");
|
|
131
|
+
file.contentType?.startsWith("audio/");
|
|
132
|
+
const FileIconComponent = getFileIcon(file.contentType);
|
|
133
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
134
|
+
const [urlCopied, setUrlCopied] = useState(false);
|
|
135
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
136
|
+
/* @__PURE__ */ jsxs("div", { className: cls("flex flex-col h-full border-l", defaultBorderMixin, "bg-white dark:bg-surface-800"), children: [
|
|
137
|
+
/* @__PURE__ */ jsxs("div", { className: cls("flex items-center justify-between p-3 border-b shrink-0", defaultBorderMixin), children: [
|
|
138
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "font-medium truncate flex-1 mr-2", children: file.name }),
|
|
139
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
|
|
140
|
+
downloadUrl && /* @__PURE__ */ jsx(Tooltip, { title: "Download", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => window.open(downloadUrl, "_blank"), children: /* @__PURE__ */ jsx(DownloadIcon, { size: iconSize.smallest }) }) }),
|
|
141
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Delete", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => setDeleteDialogOpen(true), className: "text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20", children: /* @__PURE__ */ jsx(Trash2Icon, { size: iconSize.smallest }) }) }),
|
|
142
|
+
/* @__PURE__ */ jsx(IconButton, { size: "small", onClick: onClose, children: /* @__PURE__ */ jsx(XIcon, { size: iconSize.smallest }) })
|
|
143
|
+
] })
|
|
144
|
+
] }),
|
|
145
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsx("div", { className: cls("flex flex-col items-center justify-center min-h-[200px] p-4 bg-surface-50 dark:bg-surface-800 border-b", defaultBorderMixin), children: (() => {
|
|
146
|
+
const ext = getExtension(file.name)?.toLowerCase() || "";
|
|
147
|
+
const isImage_0 = file.contentType?.startsWith("image/") || ["jpg", "jpeg", "png", "gif", "webp", "svg"].includes(ext);
|
|
148
|
+
const isVideo_0 = file.contentType?.startsWith("video/") || ["mp4", "webm", "ogg", "mov"].includes(ext);
|
|
149
|
+
const isAudio_0 = file.contentType?.startsWith("audio/") || ["mp3", "wav", "ogg", "m4a"].includes(ext);
|
|
150
|
+
const downloadUrl_0 = file.downloadUrl;
|
|
151
|
+
if (isImage_0 && downloadUrl_0) {
|
|
152
|
+
return /* @__PURE__ */ jsx("img", { src: downloadUrl_0, alt: file.name, className: "max-w-full max-h-[400px] object-contain rounded-md shadow-sm" });
|
|
153
|
+
} else if (isVideo_0 && downloadUrl_0) {
|
|
154
|
+
return /* @__PURE__ */ jsx("video", { src: downloadUrl_0, className: "max-w-full max-h-[400px] rounded-md", controls: true });
|
|
155
|
+
} else if (isAudio_0 && downloadUrl_0) {
|
|
156
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4", children: [
|
|
157
|
+
/* @__PURE__ */ jsx(Music2Icon, { className: "text-surface-accent-400 w-10 h-10" }),
|
|
158
|
+
/* @__PURE__ */ jsx("audio", { src: downloadUrl_0, controls: true, className: "w-full max-w-xs" })
|
|
159
|
+
] });
|
|
160
|
+
} else {
|
|
161
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 text-surface-accent-400", children: [
|
|
162
|
+
/* @__PURE__ */ jsx(FileIconComponent, { className: "w-10 h-10" }),
|
|
163
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark", children: "No preview available" })
|
|
164
|
+
] });
|
|
165
|
+
}
|
|
166
|
+
})() }) }),
|
|
167
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 space-y-3", children: [
|
|
168
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark text-[10px] uppercase tracking-wider font-bold mb-1 block", children: "File Info" }) }),
|
|
169
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
170
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
171
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-surface-accent-500 text-[11px]", children: "Name" }),
|
|
172
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-[13px] break-all", children: file.name })
|
|
173
|
+
] }),
|
|
174
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
175
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-surface-accent-500 text-[11px]", children: "Type" }),
|
|
176
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-[13px]", children: file.contentType || "Unknown" })
|
|
177
|
+
] }),
|
|
178
|
+
file.size !== void 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
179
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-surface-accent-500 text-[11px]", children: "Size" }),
|
|
180
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-[13px]", children: formatFileSize(file.size) })
|
|
181
|
+
] }),
|
|
182
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
183
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-surface-accent-500 text-[11px]", children: "Extension" }),
|
|
184
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-[13px] font-mono", children: getExtension(file.name) || "—" })
|
|
185
|
+
] }),
|
|
186
|
+
/* @__PURE__ */ jsxs("div", { className: "col-span-2", children: [
|
|
187
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-surface-accent-500 text-[11px]", children: "Path" }),
|
|
188
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-[13px] font-mono break-all", children: file.fullPath })
|
|
189
|
+
] })
|
|
190
|
+
] }),
|
|
191
|
+
downloadUrl && /* @__PURE__ */ jsxs("div", { className: "pt-2", children: [
|
|
192
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-surface-accent-500 text-[11px] block mb-1", children: "URL" }),
|
|
193
|
+
/* @__PURE__ */ jsxs("div", { className: cls("flex items-center gap-2 p-2 rounded cursor-pointer transition-colors", "bg-surface-100 dark:bg-surface-800 hover:bg-surface-200 dark:hover:bg-surface-700"), onClick: () => {
|
|
194
|
+
const fullUrl = downloadUrl.startsWith("http") ? downloadUrl : `${window.location.origin}${downloadUrl.startsWith("/") ? "" : "/"}${downloadUrl}`;
|
|
195
|
+
navigator.clipboard.writeText(fullUrl).then(() => {
|
|
196
|
+
setUrlCopied(true);
|
|
197
|
+
setTimeout(() => setUrlCopied(false), 2e3);
|
|
198
|
+
});
|
|
199
|
+
}, children: [
|
|
200
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-mono text-[11px] truncate flex-1 min-w-0 text-primary", children: (() => {
|
|
201
|
+
const fullUrl_0 = downloadUrl.startsWith("http") ? downloadUrl : `${window.location.origin}${downloadUrl.startsWith("/") ? "" : "/"}${downloadUrl}`;
|
|
202
|
+
return fullUrl_0;
|
|
203
|
+
})() }),
|
|
204
|
+
/* @__PURE__ */ jsx(Tooltip, { title: urlCopied ? "Copied!" : "Copy URL", children: /* @__PURE__ */ jsx("div", { className: "shrink-0", children: urlCopied ? /* @__PURE__ */ jsx(CheckIcon, { size: 14, className: "text-green-500" }) : /* @__PURE__ */ jsx(CopyIcon, { size: 14, className: "text-surface-accent-400" }) }) })
|
|
205
|
+
] })
|
|
206
|
+
] })
|
|
207
|
+
] })
|
|
208
|
+
] }),
|
|
209
|
+
/* @__PURE__ */ jsxs(Dialog, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, children: [
|
|
210
|
+
/* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
211
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", className: "mb-2", children: "Delete File?" }),
|
|
212
|
+
/* @__PURE__ */ jsxs(Typography, { className: "text-surface-accent-600 dark:text-surface-accent-400", children: [
|
|
213
|
+
'Are you sure you want to delete "',
|
|
214
|
+
file.name,
|
|
215
|
+
'"? This action cannot be undone.'
|
|
216
|
+
] })
|
|
217
|
+
] }),
|
|
218
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [
|
|
219
|
+
/* @__PURE__ */ jsx(Button, { variant: "text", onClick: () => setDeleteDialogOpen(false), children: "Cancel" }),
|
|
220
|
+
/* @__PURE__ */ jsx(Button, { variant: "filled", color: "error", onClick: () => {
|
|
221
|
+
setDeleteDialogOpen(false);
|
|
222
|
+
onDelete();
|
|
223
|
+
}, children: "Delete" })
|
|
224
|
+
] })
|
|
225
|
+
] })
|
|
226
|
+
] });
|
|
227
|
+
}
|
|
228
|
+
const StorageView = () => {
|
|
229
|
+
const storageSource = useStorageSource();
|
|
230
|
+
const snackbarController = useSnackbarController();
|
|
231
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
232
|
+
const currentPath = searchParams.get("path") || "";
|
|
233
|
+
const [loading, setLoading] = useState(true);
|
|
234
|
+
const [error, setError] = useState(null);
|
|
235
|
+
const [folders, setFolders] = useState([]);
|
|
236
|
+
const [files, setFiles] = useState([]);
|
|
237
|
+
const [selectedFile, setSelectedFile] = useState(null);
|
|
238
|
+
const [selectedDownloadUrl, setSelectedDownloadUrl] = useState(null);
|
|
239
|
+
const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
|
|
240
|
+
const [viewMode, setViewMode] = useState("grid");
|
|
241
|
+
const [selectedPaths, setSelectedPaths] = useState(/* @__PURE__ */ new Set());
|
|
242
|
+
const lastClickedRef = useRef(null);
|
|
243
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
244
|
+
const [deleteDialogTarget, setDeleteDialogTarget] = useState(null);
|
|
245
|
+
const [deleting, setDeleting] = useState(false);
|
|
246
|
+
const [newFolderDialogOpen, setNewFolderDialogOpen] = useState(false);
|
|
247
|
+
const [newFolderName, setNewFolderName] = useState("");
|
|
248
|
+
const [creatingFolder, setCreatingFolder] = useState(false);
|
|
249
|
+
const apiConfig = useApiConfig();
|
|
250
|
+
const storageSourceRef = React.useRef(storageSource);
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
storageSourceRef.current = storageSource;
|
|
253
|
+
}, [storageSource]);
|
|
254
|
+
const fetchContents = useCallback(async (path) => {
|
|
255
|
+
setLoading(true);
|
|
256
|
+
setError(null);
|
|
257
|
+
try {
|
|
258
|
+
const result = await storageSourceRef.current.listObjects(path);
|
|
259
|
+
const folderItems = (result.prefixes ?? []).map((ref) => ({
|
|
260
|
+
name: ref.name,
|
|
261
|
+
fullPath: ref.fullPath,
|
|
262
|
+
isFolder: true
|
|
263
|
+
}));
|
|
264
|
+
const fileItems = await Promise.all((result.items ?? []).map(async (ref_0) => {
|
|
265
|
+
try {
|
|
266
|
+
const downloadConfig = await storageSourceRef.current.getSignedUrl(ref_0.fullPath);
|
|
267
|
+
return {
|
|
268
|
+
name: ref_0.name,
|
|
269
|
+
fullPath: ref_0.fullPath,
|
|
270
|
+
isFolder: false,
|
|
271
|
+
size: downloadConfig.metadata?.size,
|
|
272
|
+
contentType: downloadConfig.metadata?.contentType,
|
|
273
|
+
downloadUrl: downloadConfig.url ?? void 0
|
|
274
|
+
};
|
|
275
|
+
} catch {
|
|
276
|
+
return {
|
|
277
|
+
name: ref_0.name,
|
|
278
|
+
fullPath: ref_0.fullPath,
|
|
279
|
+
isFolder: false
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}));
|
|
283
|
+
setFolders(folderItems);
|
|
284
|
+
setFiles(fileItems);
|
|
285
|
+
} catch (e) {
|
|
286
|
+
console.error("Storage list error:", e);
|
|
287
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
288
|
+
} finally {
|
|
289
|
+
setLoading(false);
|
|
290
|
+
}
|
|
291
|
+
}, []);
|
|
292
|
+
useEffect(() => {
|
|
293
|
+
fetchContents(currentPath);
|
|
294
|
+
}, [currentPath, fetchContents]);
|
|
295
|
+
const handleNavigate = useCallback((path_0) => {
|
|
296
|
+
if (!path_0) {
|
|
297
|
+
setSearchParams({});
|
|
298
|
+
} else {
|
|
299
|
+
setSearchParams({
|
|
300
|
+
path: path_0
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
setSelectedFile(null);
|
|
304
|
+
setSelectedDownloadUrl(null);
|
|
305
|
+
setSelectedPaths(/* @__PURE__ */ new Set());
|
|
306
|
+
lastClickedRef.current = null;
|
|
307
|
+
}, [setSearchParams]);
|
|
308
|
+
const handleNavigateUp = useCallback(() => {
|
|
309
|
+
const parts = currentPath.split("/").filter(Boolean);
|
|
310
|
+
parts.pop();
|
|
311
|
+
handleNavigate(parts.join("/"));
|
|
312
|
+
}, [currentPath, handleNavigate]);
|
|
313
|
+
const allItems = useMemo(() => [...folders, ...files], [folders, files]);
|
|
314
|
+
const handleItemClick = useCallback((item, e_0) => {
|
|
315
|
+
const path_1 = item.fullPath;
|
|
316
|
+
if (e_0.metaKey || e_0.ctrlKey) {
|
|
317
|
+
setSelectedPaths((prev) => {
|
|
318
|
+
const next = new Set(prev);
|
|
319
|
+
if (next.has(path_1)) next.delete(path_1);
|
|
320
|
+
else next.add(path_1);
|
|
321
|
+
return next;
|
|
322
|
+
});
|
|
323
|
+
lastClickedRef.current = path_1;
|
|
324
|
+
} else if (e_0.shiftKey && lastClickedRef.current) {
|
|
325
|
+
const allPaths = allItems.map((i) => i.fullPath);
|
|
326
|
+
const anchorIdx = allPaths.indexOf(lastClickedRef.current);
|
|
327
|
+
const currentIdx = allPaths.indexOf(path_1);
|
|
328
|
+
if (anchorIdx >= 0 && currentIdx >= 0) {
|
|
329
|
+
const [start, end] = anchorIdx < currentIdx ? [anchorIdx, currentIdx] : [currentIdx, anchorIdx];
|
|
330
|
+
setSelectedPaths((prev_0) => {
|
|
331
|
+
const next_0 = new Set(prev_0);
|
|
332
|
+
for (let i_0 = start; i_0 <= end; i_0++) next_0.add(allPaths[i_0]);
|
|
333
|
+
return next_0;
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
setSelectedPaths(/* @__PURE__ */ new Set([path_1]));
|
|
338
|
+
lastClickedRef.current = path_1;
|
|
339
|
+
if (!item.isFolder) {
|
|
340
|
+
setSelectedFile(item);
|
|
341
|
+
if (item.downloadUrl) {
|
|
342
|
+
setSelectedDownloadUrl(item.downloadUrl);
|
|
343
|
+
} else {
|
|
344
|
+
storageSourceRef.current.getSignedUrl(item.fullPath).then((config) => setSelectedDownloadUrl(config.url)).catch(() => setSelectedDownloadUrl(null));
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
setSelectedFile(null);
|
|
348
|
+
setSelectedDownloadUrl(null);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}, [allItems]);
|
|
352
|
+
const handleItemDoubleClick = useCallback((item_0) => {
|
|
353
|
+
if (item_0.isFolder) {
|
|
354
|
+
handleNavigate(item_0.fullPath);
|
|
355
|
+
} else {
|
|
356
|
+
setSelectedFile(item_0);
|
|
357
|
+
if (item_0.downloadUrl) {
|
|
358
|
+
setSelectedDownloadUrl(item_0.downloadUrl);
|
|
359
|
+
} else {
|
|
360
|
+
storageSourceRef.current.getSignedUrl(item_0.fullPath).then((config_0) => setSelectedDownloadUrl(config_0.url)).catch(() => setSelectedDownloadUrl(null));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}, [handleNavigate]);
|
|
364
|
+
const handleUpload = useCallback(async (uploadFiles) => {
|
|
365
|
+
for (const file of uploadFiles) {
|
|
366
|
+
const key = currentPath ? `${currentPath}/${file.name}` : file.name;
|
|
367
|
+
await storageSourceRef.current.putObject({
|
|
368
|
+
file,
|
|
369
|
+
key
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
snackbarController.open({
|
|
373
|
+
type: "success",
|
|
374
|
+
message: `${uploadFiles.length} file${uploadFiles.length > 1 ? "s" : ""} uploaded successfully`
|
|
375
|
+
});
|
|
376
|
+
await fetchContents(currentPath);
|
|
377
|
+
}, [currentPath, snackbarController, fetchContents]);
|
|
378
|
+
const handleCreateFolder = useCallback(async () => {
|
|
379
|
+
if (!newFolderName.trim() || !apiConfig?.apiUrl) return;
|
|
380
|
+
const name = newFolderName.trim();
|
|
381
|
+
if (name.includes("/") || name.includes("\\")) {
|
|
382
|
+
snackbarController.open({
|
|
383
|
+
type: "error",
|
|
384
|
+
message: "Folder name cannot contain slashes"
|
|
385
|
+
});
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const existingFolder = folders.find((f) => f.name === name);
|
|
389
|
+
if (existingFolder) {
|
|
390
|
+
snackbarController.open({
|
|
391
|
+
type: "error",
|
|
392
|
+
message: `Folder "${name}" already exists`
|
|
393
|
+
});
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
setCreatingFolder(true);
|
|
397
|
+
try {
|
|
398
|
+
const folderPath = currentPath ? `default/${currentPath}/${name}` : `default/${name}`;
|
|
399
|
+
const token = apiConfig.getAuthToken ? await apiConfig.getAuthToken() : null;
|
|
400
|
+
const response = await fetch(`${apiConfig.apiUrl}/api/storage/folder`, {
|
|
401
|
+
method: "POST",
|
|
402
|
+
headers: {
|
|
403
|
+
"Content-Type": "application/json",
|
|
404
|
+
...token ? {
|
|
405
|
+
"Authorization": `Bearer ${token}`
|
|
406
|
+
} : {}
|
|
407
|
+
},
|
|
408
|
+
body: JSON.stringify({
|
|
409
|
+
path: folderPath
|
|
410
|
+
})
|
|
411
|
+
});
|
|
412
|
+
if (!response.ok) {
|
|
413
|
+
const err = await response.json().catch(() => ({
|
|
414
|
+
error: "Failed to create folder"
|
|
415
|
+
}));
|
|
416
|
+
throw new Error(err.error || "Failed to create folder");
|
|
417
|
+
}
|
|
418
|
+
snackbarController.open({
|
|
419
|
+
type: "success",
|
|
420
|
+
message: `Folder "${name}" created`
|
|
421
|
+
});
|
|
422
|
+
setNewFolderDialogOpen(false);
|
|
423
|
+
setNewFolderName("");
|
|
424
|
+
await fetchContents(currentPath);
|
|
425
|
+
} catch (e_1) {
|
|
426
|
+
snackbarController.open({
|
|
427
|
+
type: "error",
|
|
428
|
+
message: e_1 instanceof Error ? e_1.message : String(e_1)
|
|
429
|
+
});
|
|
430
|
+
} finally {
|
|
431
|
+
setCreatingFolder(false);
|
|
432
|
+
}
|
|
433
|
+
}, [newFolderName, currentPath, apiConfig, snackbarController, fetchContents, folders]);
|
|
434
|
+
const handleDropFiles = useCallback(async (droppedFiles) => {
|
|
435
|
+
if (droppedFiles.length === 0) return;
|
|
436
|
+
try {
|
|
437
|
+
for (const file_0 of droppedFiles) {
|
|
438
|
+
const key_0 = currentPath ? `${currentPath}/${file_0.name}` : file_0.name;
|
|
439
|
+
await storageSourceRef.current.putObject({
|
|
440
|
+
file: file_0,
|
|
441
|
+
key: key_0
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
snackbarController.open({
|
|
445
|
+
type: "success",
|
|
446
|
+
message: `${droppedFiles.length} file${droppedFiles.length > 1 ? "s" : ""} uploaded successfully`
|
|
447
|
+
});
|
|
448
|
+
await fetchContents(currentPath);
|
|
449
|
+
} catch (e_2) {
|
|
450
|
+
snackbarController.open({
|
|
451
|
+
type: "error",
|
|
452
|
+
message: e_2 instanceof Error ? e_2.message : String(e_2)
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}, [currentPath, snackbarController, fetchContents]);
|
|
456
|
+
const {
|
|
457
|
+
getRootProps: getDropRootProps,
|
|
458
|
+
getInputProps: getDropInputProps,
|
|
459
|
+
isDragActive
|
|
460
|
+
} = useDropzone({
|
|
461
|
+
onDrop: handleDropFiles,
|
|
462
|
+
noClick: true,
|
|
463
|
+
noKeyboard: true,
|
|
464
|
+
noDragEventsBubbling: true
|
|
465
|
+
});
|
|
466
|
+
const deleteFolderRecursive = useCallback(async (prefix) => {
|
|
467
|
+
const result_0 = await storageSourceRef.current.listObjects(prefix);
|
|
468
|
+
for (const item_1 of result_0.items ?? []) {
|
|
469
|
+
await storageSourceRef.current.deleteObject(item_1.fullPath);
|
|
470
|
+
}
|
|
471
|
+
for (const sub of result_0.prefixes ?? []) {
|
|
472
|
+
await deleteFolderRecursive(sub.fullPath);
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
await storageSourceRef.current.deleteObject(prefix);
|
|
476
|
+
} catch {
|
|
477
|
+
}
|
|
478
|
+
}, []);
|
|
479
|
+
const handleDeleteFile = useCallback(async (file_1) => {
|
|
480
|
+
try {
|
|
481
|
+
if (file_1.isFolder) {
|
|
482
|
+
await deleteFolderRecursive(file_1.fullPath);
|
|
483
|
+
} else {
|
|
484
|
+
await storageSourceRef.current.deleteObject(file_1.fullPath);
|
|
485
|
+
}
|
|
486
|
+
snackbarController.open({
|
|
487
|
+
type: "success",
|
|
488
|
+
message: `"${file_1.name}" deleted`
|
|
489
|
+
});
|
|
490
|
+
setSelectedFile(null);
|
|
491
|
+
setSelectedDownloadUrl(null);
|
|
492
|
+
setSelectedPaths((prev_1) => {
|
|
493
|
+
const next_1 = new Set(prev_1);
|
|
494
|
+
next_1.delete(file_1.fullPath);
|
|
495
|
+
return next_1;
|
|
496
|
+
});
|
|
497
|
+
fetchContents(currentPath);
|
|
498
|
+
} catch (e_3) {
|
|
499
|
+
snackbarController.open({
|
|
500
|
+
type: "error",
|
|
501
|
+
message: e_3 instanceof Error ? e_3.message : String(e_3)
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}, [currentPath, snackbarController, fetchContents, deleteFolderRecursive]);
|
|
505
|
+
const handleBulkDelete = useCallback(async () => {
|
|
506
|
+
setDeleting(true);
|
|
507
|
+
try {
|
|
508
|
+
const items = allItems.filter((i_1) => selectedPaths.has(i_1.fullPath));
|
|
509
|
+
for (const item_2 of items) {
|
|
510
|
+
if (item_2.isFolder) {
|
|
511
|
+
await deleteFolderRecursive(item_2.fullPath);
|
|
512
|
+
} else {
|
|
513
|
+
await storageSourceRef.current.deleteObject(item_2.fullPath);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
snackbarController.open({
|
|
517
|
+
type: "success",
|
|
518
|
+
message: `${items.length} item${items.length !== 1 ? "s" : ""} deleted`
|
|
519
|
+
});
|
|
520
|
+
setSelectedPaths(/* @__PURE__ */ new Set());
|
|
521
|
+
setSelectedFile(null);
|
|
522
|
+
setSelectedDownloadUrl(null);
|
|
523
|
+
await fetchContents(currentPath);
|
|
524
|
+
} catch (e_4) {
|
|
525
|
+
snackbarController.open({
|
|
526
|
+
type: "error",
|
|
527
|
+
message: e_4 instanceof Error ? e_4.message : String(e_4)
|
|
528
|
+
});
|
|
529
|
+
} finally {
|
|
530
|
+
setDeleting(false);
|
|
531
|
+
setDeleteDialogOpen(false);
|
|
532
|
+
setDeleteDialogTarget(null);
|
|
533
|
+
}
|
|
534
|
+
}, [allItems, selectedPaths, currentPath, snackbarController, fetchContents, deleteFolderRecursive]);
|
|
535
|
+
const handleConfirmDeleteFolder = useCallback(async () => {
|
|
536
|
+
if (!deleteDialogTarget || deleteDialogTarget === "selection") return;
|
|
537
|
+
setDeleting(true);
|
|
538
|
+
try {
|
|
539
|
+
await deleteFolderRecursive(deleteDialogTarget.fullPath);
|
|
540
|
+
snackbarController.open({
|
|
541
|
+
type: "success",
|
|
542
|
+
message: `Folder "${deleteDialogTarget.name}" deleted`
|
|
543
|
+
});
|
|
544
|
+
setSelectedPaths((prev_2) => {
|
|
545
|
+
const next_2 = new Set(prev_2);
|
|
546
|
+
next_2.delete(deleteDialogTarget.fullPath);
|
|
547
|
+
return next_2;
|
|
548
|
+
});
|
|
549
|
+
await fetchContents(currentPath);
|
|
550
|
+
} catch (e_5) {
|
|
551
|
+
snackbarController.open({
|
|
552
|
+
type: "error",
|
|
553
|
+
message: e_5 instanceof Error ? e_5.message : String(e_5)
|
|
554
|
+
});
|
|
555
|
+
} finally {
|
|
556
|
+
setDeleting(false);
|
|
557
|
+
setDeleteDialogOpen(false);
|
|
558
|
+
setDeleteDialogTarget(null);
|
|
559
|
+
}
|
|
560
|
+
}, [deleteDialogTarget, currentPath, snackbarController, fetchContents, deleteFolderRecursive]);
|
|
561
|
+
const handleSelectAll = useCallback(() => {
|
|
562
|
+
if (selectedPaths.size === allItems.length) {
|
|
563
|
+
setSelectedPaths(/* @__PURE__ */ new Set());
|
|
564
|
+
} else {
|
|
565
|
+
setSelectedPaths(new Set(allItems.map((i_2) => i_2.fullPath)));
|
|
566
|
+
}
|
|
567
|
+
}, [allItems, selectedPaths]);
|
|
568
|
+
useEffect(() => {
|
|
569
|
+
const handler = (e_6) => {
|
|
570
|
+
if (deleteDialogOpen || uploadDialogOpen || newFolderDialogOpen) return;
|
|
571
|
+
if ((e_6.metaKey || e_6.ctrlKey) && e_6.key === "a") {
|
|
572
|
+
e_6.preventDefault();
|
|
573
|
+
handleSelectAll();
|
|
574
|
+
}
|
|
575
|
+
if (e_6.key === "Escape") {
|
|
576
|
+
setSelectedPaths(/* @__PURE__ */ new Set());
|
|
577
|
+
setSelectedFile(null);
|
|
578
|
+
setSelectedDownloadUrl(null);
|
|
579
|
+
}
|
|
580
|
+
if ((e_6.key === "Delete" || e_6.key === "Backspace") && selectedPaths.size > 0 && !e_6.metaKey && !e_6.ctrlKey) {
|
|
581
|
+
if (e_6.target?.tagName === "INPUT" || e_6.target?.tagName === "TEXTAREA") return;
|
|
582
|
+
e_6.preventDefault();
|
|
583
|
+
setDeleteDialogTarget("selection");
|
|
584
|
+
setDeleteDialogOpen(true);
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
window.addEventListener("keydown", handler);
|
|
588
|
+
return () => window.removeEventListener("keydown", handler);
|
|
589
|
+
}, [handleSelectAll, selectedPaths, deleteDialogOpen, uploadDialogOpen, newFolderDialogOpen]);
|
|
590
|
+
const handleRefresh = useCallback(() => {
|
|
591
|
+
fetchContents(currentPath);
|
|
592
|
+
}, [currentPath, fetchContents]);
|
|
593
|
+
const segments = breadcrumbSegments(currentPath);
|
|
594
|
+
const renderContents = () => {
|
|
595
|
+
if (loading) {
|
|
596
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-grow flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
597
|
+
/* @__PURE__ */ jsx(CircularProgress, { size: "medium" }),
|
|
598
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "mt-4 text-text-secondary dark:text-text-secondary-dark font-mono tracking-tight animate-pulse", children: "Loading..." })
|
|
599
|
+
] }) });
|
|
600
|
+
}
|
|
601
|
+
if (error) {
|
|
602
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-grow flex items-center justify-center p-6 overflow-auto", children: /* @__PURE__ */ jsx(ErrorView, { title: "Error loading storage", error, onRetry: handleRefresh }) });
|
|
603
|
+
}
|
|
604
|
+
if (allItems.length === 0) {
|
|
605
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-grow flex items-center justify-center text-text-disabled dark:text-text-disabled-dark", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
606
|
+
/* @__PURE__ */ jsx("svg", { className: "w-12 h-12 mx-auto mb-4 opacity-50", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1, d: "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" }) }),
|
|
607
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", children: "This folder is empty" }),
|
|
608
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-3", children: [
|
|
609
|
+
/* @__PURE__ */ jsxs(Button, { variant: "text", onClick: () => {
|
|
610
|
+
setNewFolderName("");
|
|
611
|
+
setNewFolderDialogOpen(true);
|
|
612
|
+
}, children: [
|
|
613
|
+
/* @__PURE__ */ jsx(FolderPlusIcon, { size: iconSize.smallest }),
|
|
614
|
+
"New folder"
|
|
615
|
+
] }),
|
|
616
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => setUploadDialogOpen(true), children: [
|
|
617
|
+
/* @__PURE__ */ jsx(PlusIcon, { size: iconSize.smallest }),
|
|
618
|
+
"Upload files"
|
|
619
|
+
] })
|
|
620
|
+
] })
|
|
621
|
+
] }) });
|
|
622
|
+
}
|
|
623
|
+
if (viewMode === "list") {
|
|
624
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-grow overflow-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
|
|
625
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: cls("border-b text-left text-[10px] uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark", defaultBorderMixin), children: [
|
|
626
|
+
/* @__PURE__ */ jsx("th", { className: "pl-3 pr-0 py-2 w-8", children: /* @__PURE__ */ jsx(Checkbox, { size: "small", checked: allItems.length > 0 && selectedPaths.size === allItems.length, indeterminate: selectedPaths.size > 0 && selectedPaths.size < allItems.length, onCheckedChange: handleSelectAll }) }),
|
|
627
|
+
/* @__PURE__ */ jsx("th", { className: "px-2 py-2 font-bold", children: "Name" }),
|
|
628
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-2 font-bold w-24", children: "Type" }),
|
|
629
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-2 font-bold w-24 text-right", children: "Size" }),
|
|
630
|
+
/* @__PURE__ */ jsx("th", { className: "px-2 py-2 w-10" })
|
|
631
|
+
] }) }),
|
|
632
|
+
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
633
|
+
folders.map((folder) => {
|
|
634
|
+
const isChecked = selectedPaths.has(folder.fullPath);
|
|
635
|
+
return /* @__PURE__ */ jsxs("tr", { "data-storage-item": true, className: cls("cursor-pointer transition-colors border-b group", defaultBorderMixin, isChecked ? "bg-primary/5 dark:bg-primary/10" : "hover:bg-surface-100 dark:hover:bg-surface-800"), onClick: (e_7) => handleItemClick(folder, e_7), onDoubleClick: () => handleItemDoubleClick(folder), children: [
|
|
636
|
+
/* @__PURE__ */ jsx("td", { className: "pl-3 pr-0 py-2.5", onClick: (e_8) => e_8.stopPropagation(), children: /* @__PURE__ */ jsx(Checkbox, { size: "small", checked: isChecked, onCheckedChange: () => {
|
|
637
|
+
setSelectedPaths((prev_3) => {
|
|
638
|
+
const next_3 = new Set(prev_3);
|
|
639
|
+
if (next_3.has(folder.fullPath)) next_3.delete(folder.fullPath);
|
|
640
|
+
else next_3.add(folder.fullPath);
|
|
641
|
+
return next_3;
|
|
642
|
+
});
|
|
643
|
+
} }) }),
|
|
644
|
+
/* @__PURE__ */ jsx("td", { className: "px-2 py-2.5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
645
|
+
/* @__PURE__ */ jsx(FolderIcon, { size: iconSize.smallest, className: "text-amber-500 dark:text-amber-400 shrink-0" }),
|
|
646
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-[13px] font-medium truncate", children: folder.name })
|
|
647
|
+
] }) }),
|
|
648
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2.5", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-secondary dark:text-text-secondary-dark", children: "Folder" }) }),
|
|
649
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2.5 text-right", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark", children: "—" }) }),
|
|
650
|
+
/* @__PURE__ */ jsx("td", { className: "px-2 py-2.5" })
|
|
651
|
+
] }, folder.fullPath);
|
|
652
|
+
}),
|
|
653
|
+
files.map((file_2) => {
|
|
654
|
+
const FileIconComp = getFileIcon(file_2.contentType);
|
|
655
|
+
const isChecked_0 = selectedPaths.has(file_2.fullPath);
|
|
656
|
+
return /* @__PURE__ */ jsxs("tr", { "data-storage-item": true, className: cls("cursor-pointer transition-colors border-b group", defaultBorderMixin, isChecked_0 ? "bg-primary/5 dark:bg-primary/10" : "hover:bg-surface-100 dark:hover:bg-surface-800"), onClick: (e_9) => handleItemClick(file_2, e_9), onDoubleClick: () => handleItemDoubleClick(file_2), children: [
|
|
657
|
+
/* @__PURE__ */ jsx("td", { className: "pl-3 pr-0 py-2.5", onClick: (e_10) => e_10.stopPropagation(), children: /* @__PURE__ */ jsx(Checkbox, { size: "small", checked: isChecked_0, onCheckedChange: () => {
|
|
658
|
+
setSelectedPaths((prev_4) => {
|
|
659
|
+
const next_4 = new Set(prev_4);
|
|
660
|
+
if (next_4.has(file_2.fullPath)) next_4.delete(file_2.fullPath);
|
|
661
|
+
else next_4.add(file_2.fullPath);
|
|
662
|
+
return next_4;
|
|
663
|
+
});
|
|
664
|
+
} }) }),
|
|
665
|
+
/* @__PURE__ */ jsx("td", { className: "px-2 py-2.5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
666
|
+
/* @__PURE__ */ jsx(FileIconComp, { size: iconSize.smallest, className: "text-surface-accent-400 shrink-0" }),
|
|
667
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-[13px] truncate", children: file_2.name })
|
|
668
|
+
] }) }),
|
|
669
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2.5", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-secondary dark:text-text-secondary-dark", children: getExtension(file_2.name) || file_2.contentType?.split("/")[1]?.toUpperCase() || "—" }) }),
|
|
670
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2.5 text-right", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-secondary dark:text-text-secondary-dark font-mono text-[11px]", children: file_2.size !== void 0 ? formatFileSize(file_2.size) : "—" }) }),
|
|
671
|
+
/* @__PURE__ */ jsx("td", { className: "px-2 py-2.5", onClick: (e_11) => e_11.stopPropagation(), children: /* @__PURE__ */ jsx(IconButton, { size: "smallest", className: "opacity-0 group-hover:opacity-100 transition-opacity", onClick: () => handleDeleteFile(file_2), children: /* @__PURE__ */ jsx(Trash2Icon, { size: 14 }) }) })
|
|
672
|
+
] }, file_2.fullPath);
|
|
673
|
+
})
|
|
674
|
+
] })
|
|
675
|
+
] }) });
|
|
676
|
+
}
|
|
677
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex-grow overflow-auto p-4", children: [
|
|
678
|
+
folders.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
679
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[10px] uppercase tracking-wider font-bold text-text-disabled dark:text-text-disabled-dark mb-2 block", children: "Folders" }),
|
|
680
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]", children: folders.map((folder_0) => {
|
|
681
|
+
const isChecked_1 = selectedPaths.has(folder_0.fullPath);
|
|
682
|
+
return /* @__PURE__ */ jsxs("div", { "data-storage-item": true, className: cls("rounded-lg p-3 cursor-pointer border", "transition-colors duration-150", defaultBorderMixin, "hover:bg-surface-100 dark:hover:bg-surface-800 hover:shadow-sm", "flex items-center gap-2", isChecked_1 && "ring-2 ring-primary bg-primary/5 dark:bg-primary/10"), onClick: (e_12) => handleItemClick(folder_0, e_12), onDoubleClick: () => handleItemDoubleClick(folder_0), children: [
|
|
683
|
+
/* @__PURE__ */ jsx(FolderIcon, { size: iconSize.smallest, className: "text-amber-500 dark:text-amber-400 shrink-0" }),
|
|
684
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-[13px] font-medium truncate", children: folder_0.name })
|
|
685
|
+
] }, folder_0.fullPath);
|
|
686
|
+
}) })
|
|
687
|
+
] }),
|
|
688
|
+
files.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
689
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-[10px] uppercase tracking-wider font-bold text-text-disabled dark:text-text-disabled-dark mb-2 block", children: [
|
|
690
|
+
"Files (",
|
|
691
|
+
files.length,
|
|
692
|
+
")"
|
|
693
|
+
] }),
|
|
694
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]", children: files.map((file_3) => {
|
|
695
|
+
const FileIconComp_0 = getFileIcon(file_3.contentType);
|
|
696
|
+
const ext = getExtension(file_3.name)?.toLowerCase() || "";
|
|
697
|
+
const isImage = file_3.contentType?.startsWith("image/") || ["jpg", "jpeg", "png", "gif", "webp", "svg"].includes(ext);
|
|
698
|
+
const isChecked_2 = selectedPaths.has(file_3.fullPath);
|
|
699
|
+
return /* @__PURE__ */ jsxs("div", { "data-storage-item": true, className: cls("rounded-lg overflow-hidden cursor-pointer border", "transition-shadow duration-150", defaultBorderMixin, "hover:shadow-md", isChecked_2 && "ring-2 ring-primary"), onClick: (e_13) => handleItemClick(file_3, e_13), onDoubleClick: () => handleItemDoubleClick(file_3), children: [
|
|
700
|
+
/* @__PURE__ */ jsxs("div", { className: "aspect-square relative overflow-hidden bg-surface-100 dark:bg-surface-800 flex items-center justify-center", children: [
|
|
701
|
+
isImage && file_3.downloadUrl ? /* @__PURE__ */ jsx("img", { src: file_3.downloadUrl, alt: file_3.name, className: "w-full h-full object-cover", loading: "lazy" }) : /* @__PURE__ */ jsx(FileIconComp_0, { className: "text-surface-accent-400 dark:text-surface-accent-500 w-8 h-8" }),
|
|
702
|
+
getExtension(file_3.name) && /* @__PURE__ */ jsx("div", { className: "absolute bottom-1.5 right-1.5 px-1.5 py-0.5 rounded text-[9px] font-bold uppercase bg-black/50 text-white backdrop-blur-sm", children: getExtension(file_3.name) })
|
|
703
|
+
] }),
|
|
704
|
+
/* @__PURE__ */ jsxs("div", { className: "p-2.5", children: [
|
|
705
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-[12px] font-medium truncate text-surface-900 dark:text-white", children: file_3.name }),
|
|
706
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "truncate block mt-0.5 text-[11px]", children: file_3.size !== void 0 ? formatFileSize(file_3.size) : "—" })
|
|
707
|
+
] })
|
|
708
|
+
] }, file_3.fullPath);
|
|
709
|
+
}) })
|
|
710
|
+
] })
|
|
711
|
+
] });
|
|
712
|
+
};
|
|
713
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full bg-white dark:bg-surface-800 overflow-hidden text-text-primary dark:text-text-primary-dark", children: [
|
|
714
|
+
/* @__PURE__ */ jsxs("div", { className: "flex h-full w-full", children: [
|
|
715
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-grow flex flex-col min-w-0 h-full", children: [
|
|
716
|
+
/* @__PURE__ */ jsxs("div", { className: cls("flex items-center justify-between pr-2 border-b bg-white dark:bg-surface-800 shrink-0 h-10", defaultBorderMixin), children: [
|
|
717
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 flex-grow overflow-hidden px-3 py-2", children: [
|
|
718
|
+
currentPath && /* @__PURE__ */ jsx(Tooltip, { title: "Go up", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handleNavigateUp, children: /* @__PURE__ */ jsx(ArrowLeftIcon, { size: iconSize.smallest }) }) }),
|
|
719
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5 overflow-x-auto no-scrollbar", children: segments.map((seg, i_3) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
720
|
+
i_3 > 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-text-disabled dark:text-text-disabled-dark mx-0.5", children: "/" }),
|
|
721
|
+
/* @__PURE__ */ jsx(Button, { variant: "text", size: "small", className: cls("px-1.5 py-0.5 min-h-0 min-w-0 h-6 text-xs whitespace-nowrap normal-case font-normal", i_3 === segments.length - 1 ? "text-text-primary dark:text-text-primary-dark font-medium" : "text-text-secondary dark:text-text-secondary-dark"), onClick: () => handleNavigate(seg.path), children: seg.label })
|
|
722
|
+
] }, seg.path)) }),
|
|
723
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
724
|
+
selectedPaths.size > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
|
|
725
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", className: "text-[13px] font-medium whitespace-nowrap", children: [
|
|
726
|
+
selectedPaths.size,
|
|
727
|
+
" selected"
|
|
728
|
+
] }),
|
|
729
|
+
/* @__PURE__ */ jsxs(Button, { size: "small", variant: "text", onClick: () => {
|
|
730
|
+
setDeleteDialogTarget("selection");
|
|
731
|
+
setDeleteDialogOpen(true);
|
|
732
|
+
}, children: [
|
|
733
|
+
/* @__PURE__ */ jsx(Trash2Icon, { size: 14, className: "mr-1" }),
|
|
734
|
+
"Delete"
|
|
735
|
+
] }),
|
|
736
|
+
/* @__PURE__ */ jsxs(Button, { size: "small", variant: "text", onClick: () => {
|
|
737
|
+
setSelectedPaths(/* @__PURE__ */ new Set());
|
|
738
|
+
setSelectedFile(null);
|
|
739
|
+
setSelectedDownloadUrl(null);
|
|
740
|
+
}, children: [
|
|
741
|
+
/* @__PURE__ */ jsx(XIcon, { size: 14, className: "mr-1" }),
|
|
742
|
+
"Deselect"
|
|
743
|
+
] })
|
|
744
|
+
] }) : !loading ? /* @__PURE__ */ jsxs(Chip, { size: "small", className: "shrink-0 text-[10px]", children: [
|
|
745
|
+
files.length,
|
|
746
|
+
" file",
|
|
747
|
+
files.length !== 1 ? "s" : "",
|
|
748
|
+
folders.length > 0 ? `, ${folders.length} folder${folders.length !== 1 ? "s" : ""}` : ""
|
|
749
|
+
] }) : null
|
|
750
|
+
] }),
|
|
751
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-end gap-1.5 pr-1", children: [
|
|
752
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Grid view", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => setViewMode("grid"), className: cls(viewMode === "grid" && "bg-surface-100 dark:bg-surface-800"), children: /* @__PURE__ */ jsx(LayoutGridIcon, { size: iconSize.smallest }) }) }),
|
|
753
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "List view", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => setViewMode("list"), className: cls(viewMode === "list" && "bg-surface-100 dark:bg-surface-800"), children: /* @__PURE__ */ jsx(ListIcon, { size: iconSize.smallest }) }) }),
|
|
754
|
+
/* @__PURE__ */ jsx("div", { className: cls("h-4 w-px mx-0.5", defaultBorderMixin, "bg-surface-200 dark:bg-surface-700") }),
|
|
755
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Refresh", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handleRefresh, disabled: loading, children: /* @__PURE__ */ jsx(RefreshCwIcon, { size: iconSize.smallest }) }) }),
|
|
756
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "New folder", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => {
|
|
757
|
+
setNewFolderName("");
|
|
758
|
+
setNewFolderDialogOpen(true);
|
|
759
|
+
}, children: /* @__PURE__ */ jsx(FolderPlusIcon, { size: iconSize.smallest }) }) }),
|
|
760
|
+
/* @__PURE__ */ jsxs(Button, { size: "small", color: "primary", onClick: () => setUploadDialogOpen(true), children: [
|
|
761
|
+
/* @__PURE__ */ jsx(UploadCloudIcon, { size: iconSize.smallest, className: "mr-1" }),
|
|
762
|
+
"Upload"
|
|
763
|
+
] })
|
|
764
|
+
] })
|
|
765
|
+
] }),
|
|
766
|
+
/* @__PURE__ */ jsxs("div", { ...getDropRootProps(), className: "flex-grow flex flex-col overflow-hidden min-h-0 relative", onClick: (e_14) => {
|
|
767
|
+
const target = e_14.target;
|
|
768
|
+
if (!target.closest("[data-storage-item]") && selectedPaths.size > 0) {
|
|
769
|
+
setSelectedPaths(/* @__PURE__ */ new Set());
|
|
770
|
+
setSelectedFile(null);
|
|
771
|
+
setSelectedDownloadUrl(null);
|
|
772
|
+
}
|
|
773
|
+
}, children: [
|
|
774
|
+
/* @__PURE__ */ jsx("input", { ...getDropInputProps() }),
|
|
775
|
+
renderContents(),
|
|
776
|
+
isDragActive && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-primary/5 dark:bg-primary/10 backdrop-blur-[2px]", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 p-6 rounded-xl border-2 border-dashed border-primary bg-white/80 dark:bg-surface-900/80", children: [
|
|
777
|
+
/* @__PURE__ */ jsx(UploadCloudIcon, { className: "w-10 h-10 text-primary" }),
|
|
778
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle2", className: "text-primary font-semibold", children: "Drop files to upload" }),
|
|
779
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
|
|
780
|
+
"to /",
|
|
781
|
+
currentPath || "root"
|
|
782
|
+
] })
|
|
783
|
+
] }) })
|
|
784
|
+
] }),
|
|
785
|
+
/* @__PURE__ */ jsxs("div", { className: cls("px-4 py-1.5 border-t bg-surface-50 dark:bg-surface-800 flex items-center justify-between shrink-0", defaultBorderMixin), children: [
|
|
786
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 text-[11px]", children: [
|
|
787
|
+
/* @__PURE__ */ jsx("span", { className: "text-text-disabled dark:text-text-disabled-dark font-bold uppercase tracking-tighter", children: "Path" }),
|
|
788
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-text-secondary dark:text-text-secondary-dark", children: [
|
|
789
|
+
"/",
|
|
790
|
+
currentPath || ""
|
|
791
|
+
] })
|
|
792
|
+
] }),
|
|
793
|
+
selectedPaths.size > 0 ? /* @__PURE__ */ jsxs("div", { className: "text-[11px] text-text-secondary dark:text-text-secondary-dark", children: [
|
|
794
|
+
selectedPaths.size,
|
|
795
|
+
" item",
|
|
796
|
+
selectedPaths.size !== 1 ? "s" : "",
|
|
797
|
+
" selected"
|
|
798
|
+
] }) : selectedFile ? /* @__PURE__ */ jsxs("div", { className: "text-[11px] text-text-secondary dark:text-text-secondary-dark", children: [
|
|
799
|
+
"Selected: ",
|
|
800
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: selectedFile.name })
|
|
801
|
+
] }) : null
|
|
802
|
+
] })
|
|
803
|
+
] }),
|
|
804
|
+
selectedFile && /* @__PURE__ */ jsx("div", { className: "w-80 lg:w-96 shrink-0", children: /* @__PURE__ */ jsx(FilePreviewPanel, { file: selectedFile, downloadUrl: selectedDownloadUrl, onClose: () => {
|
|
805
|
+
setSelectedFile(null);
|
|
806
|
+
setSelectedDownloadUrl(null);
|
|
807
|
+
}, onDelete: () => handleDeleteFile(selectedFile) }) })
|
|
808
|
+
] }),
|
|
809
|
+
/* @__PURE__ */ jsx(UploadDialog, { open: uploadDialogOpen, currentPath, onClose: () => setUploadDialogOpen(false), onUpload: handleUpload }),
|
|
810
|
+
/* @__PURE__ */ jsxs(Dialog, { open: deleteDialogOpen, onOpenChange: (open) => {
|
|
811
|
+
if (!open && !deleting) {
|
|
812
|
+
setDeleteDialogOpen(false);
|
|
813
|
+
setDeleteDialogTarget(null);
|
|
814
|
+
}
|
|
815
|
+
}, children: [
|
|
816
|
+
/* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
817
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", className: "font-semibold mb-2", children: deleteDialogTarget === "selection" ? `Delete ${selectedPaths.size} item${selectedPaths.size !== 1 ? "s" : ""}?` : deleteDialogTarget ? `Delete folder "${deleteDialogTarget.name}"?` : "Delete?" }),
|
|
818
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "secondary", children: deleteDialogTarget === "selection" ? "This will permanently delete all selected files and folders, including their contents. This action cannot be undone." : "This will permanently delete the folder and all of its contents. This action cannot be undone." })
|
|
819
|
+
] }),
|
|
820
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [
|
|
821
|
+
/* @__PURE__ */ jsx(Button, { variant: "text", onClick: () => {
|
|
822
|
+
setDeleteDialogOpen(false);
|
|
823
|
+
setDeleteDialogTarget(null);
|
|
824
|
+
}, disabled: deleting, children: "Cancel" }),
|
|
825
|
+
/* @__PURE__ */ jsxs(LoadingButton, { color: "error", loading: deleting, onClick: deleteDialogTarget === "selection" ? handleBulkDelete : handleConfirmDeleteFolder, children: [
|
|
826
|
+
/* @__PURE__ */ jsx(Trash2Icon, { size: 14, className: "mr-1" }),
|
|
827
|
+
"Delete"
|
|
828
|
+
] })
|
|
829
|
+
] })
|
|
830
|
+
] }),
|
|
831
|
+
/* @__PURE__ */ jsxs(Dialog, { open: newFolderDialogOpen, onOpenChange: (open_0) => {
|
|
832
|
+
if (!open_0 && !creatingFolder) {
|
|
833
|
+
setNewFolderDialogOpen(false);
|
|
834
|
+
setNewFolderName("");
|
|
835
|
+
}
|
|
836
|
+
}, children: [
|
|
837
|
+
/* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
838
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", className: "font-semibold mb-4", children: "New Folder" }),
|
|
839
|
+
/* @__PURE__ */ jsx(TextField, { autoFocus: true, size: "small", label: "Folder name", value: newFolderName, onChange: (e_15) => setNewFolderName(e_15.target.value), onKeyDown: (e_16) => {
|
|
840
|
+
if (e_16.key === "Enter" && newFolderName.trim()) {
|
|
841
|
+
e_16.preventDefault();
|
|
842
|
+
handleCreateFolder();
|
|
843
|
+
}
|
|
844
|
+
}, disabled: creatingFolder, placeholder: "Enter folder name" }),
|
|
845
|
+
currentPath && /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", className: "mt-2", children: [
|
|
846
|
+
"Will be created in ",
|
|
847
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
848
|
+
"/",
|
|
849
|
+
currentPath,
|
|
850
|
+
"/"
|
|
851
|
+
] })
|
|
852
|
+
] })
|
|
853
|
+
] }),
|
|
854
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [
|
|
855
|
+
/* @__PURE__ */ jsx(Button, { variant: "text", onClick: () => {
|
|
856
|
+
setNewFolderDialogOpen(false);
|
|
857
|
+
setNewFolderName("");
|
|
858
|
+
}, disabled: creatingFolder, children: "Cancel" }),
|
|
859
|
+
/* @__PURE__ */ jsxs(LoadingButton, { color: "primary", loading: creatingFolder, disabled: !newFolderName.trim(), onClick: handleCreateFolder, children: [
|
|
860
|
+
/* @__PURE__ */ jsx(FolderPlusIcon, { size: 14, className: "mr-1" }),
|
|
861
|
+
"Create"
|
|
862
|
+
] })
|
|
863
|
+
] })
|
|
864
|
+
] })
|
|
865
|
+
] });
|
|
866
|
+
};
|
|
867
|
+
export {
|
|
868
|
+
StorageView
|
|
869
|
+
};
|
|
870
|
+
//# sourceMappingURL=StorageView-BYoslzBR.js.map
|