|
{f.name}
- {f.ext}
+ 첨부 {(f.attachments || []).length}개
|
{cats.find(c=>c.id===f.category)?.label || f.category} |
{f.author} |
{visMap[f.visibility] || f.visibility} |
{f.size_label} |
- ↓ {f.downloads} |
+
+
+ {(f.attachments || []).map(a => (
+ e.stopPropagation()}>
+
+
+ ))}
+ {(!f.attachments || f.attachments.length === 0) && —}
+
+ |
@@ -589,7 +758,7 @@ const FileSection = ({ showToast }) => {
{editModal && (
setEditModal(null)} onSave={saveEdit}>
-
+
@@ -617,6 +786,15 @@ const FileSection = ({ showToast }) => {
)}
+
+ {uploadOpen && (
+ { setUploadOpen(false); load(); }}
+ onUploaded={() => load()}
+ categories={cats}
+ users={users}
+ />
+ )}
);
};
diff --git a/index.html b/index.html
index 5259ef1..3a82786 100644
--- a/index.html
+++ b/index.html
@@ -143,7 +143,7 @@
/* LIST */
.list{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden}
- .list-head,.list-row{display:grid;grid-template-columns:32px 1fr 100px 130px 130px 70px 32px;gap:14px;align-items:center;padding:10px 16px}
+ .list-head,.list-row{display:grid;grid-template-columns:32px 1fr 100px 130px 130px 118px 32px;gap:14px;align-items:center;padding:10px 16px}
.list-head{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--faint);border-bottom:1px solid var(--border);background:var(--surface-2)}
.list-row{border-bottom:1px solid var(--border);font-size:13.5px;color:var(--ink-2);cursor:default;transition:background .12s ease}
.list-row:last-child{border-bottom:0}
@@ -160,18 +160,20 @@
.list-name .ftxt .nm{font-weight:500;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.list-name .ftxt .pth{font-size:11px;color:var(--faint);font-family:var(--font-mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.list-meta{font-family:var(--font-mono);font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
+ .list-dl{display:flex;align-items:center;gap:4px}
+ .dlbtn{width:22px;height:22px;border:1px solid var(--border-strong);border-radius:5px;color:var(--muted);background:var(--surface);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}
+ .dlbtn:hover{filter:brightness(0.96)}
+ .dlbtn.dlbtn-doc{background:#fef4e6;color:#b56b18;border-color:transparent}
+ .dlbtn.dlbtn-img{background:#e8f4ee;color:#1f7a52;border-color:transparent}
+ .dlbtn.dlbtn-vid{background:#1a1a1a;color:#fff;border-color:transparent}
+ .dlbtn.dlbtn-zip{background:#f3eed9;color:#7a6418;border-color:transparent}
+ .dlbtn.dlbtn-code{background:#0f1115;color:#a3b8ff;border-color:transparent}
.check{width:16px;height:16px;border-radius:4px;border:1.5px solid var(--border-strong);display:grid;place-items:center;cursor:default;color:transparent;background:var(--surface);transition:all .12s ease}
.check:hover{border-color:var(--ink-2)}
.check.on{background:var(--accent);border-color:var(--accent);color:#fff}
.iconbtn{appearance:none;border:0;background:transparent;color:var(--faint);width:28px;height:28px;border-radius:5px;cursor:default;display:grid;place-items:center}
.iconbtn:hover{background:rgba(20,20,20,.06);color:var(--ink)}
- /* Drop zone */
- .dropzone{position:absolute;inset:0;background:rgba(59,91,219,.06);backdrop-filter:blur(2px);border:2px dashed var(--accent);border-radius:18px;margin:12px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;pointer-events:none;z-index:200;opacity:0;transition:opacity .15s ease}
- .dropzone.on{opacity:1}
- .dropzone .t{font-size:18px;font-weight:600;color:var(--accent)}
- .dropzone .s{font-size:13px;color:var(--ink-2);font-family:var(--font-mono)}
-
/* Selection bar */
.selbar{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);background:var(--ink);color:#fff;padding:8px 8px 8px 18px;border-radius:99px;display:flex;align-items:center;gap:14px;box-shadow:var(--shadow-lg);z-index:100;font-size:13.5px}
.selbar .selcount{font-weight:500}
@@ -188,19 +190,6 @@
.modal-h{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border)}
.modal-h h3{margin:0;font-size:15px;font-weight:600}
.modal-b{padding:20px;overflow-y:auto}
- .updz{border:2px dashed var(--border-strong);border-radius:12px;padding:36px 20px;text-align:center;color:var(--muted);transition:all .15s ease;background:var(--surface-2)}
- .updz.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent)}
- .updz .t{font-size:15px;font-weight:600;color:var(--ink);margin-bottom:4px}
- .updz .s{font-size:12.5px;color:var(--muted);font-family:var(--font-mono)}
- .updz button{margin-top:14px}
- .uplist{margin-top:14px;display:flex;flex-direction:column;gap:8px}
- .uprow{display:flex;align-items:center;gap:12px;padding:10px 12px;background:var(--surface-2);border-radius:8px}
- .uprow .ficon{width:32px;height:32px;border-radius:6px;background:var(--surface);display:grid;place-items:center;font-family:var(--font-mono);font-size:9px;font-weight:600;color:var(--muted);flex-shrink:0}
- .uprow .info{flex:1;min-width:0}
- .uprow .nm{font-size:13px;font-weight:500;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
- .uprow .progbar{height:3px;background:rgba(20,20,20,.08);border-radius:99px;margin-top:5px;overflow:hidden}
- .uprow .progfill{height:100%;background:var(--accent);transition:width .3s ease}
- .uprow .pct{font-family:var(--font-mono);font-size:11px;color:var(--muted);min-width:36px;text-align:right}
/* Preview modal */
.preview-modal{width:min(960px,94vw);max-height:90vh}
@@ -217,6 +206,8 @@
.preview-info .infogrp{display:flex;flex-direction:column;gap:7px}
.preview-info .infogrp .lbl{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--faint)}
.preview-info .row{display:flex;justify-content:space-between;font-size:13px;color:var(--ink-2);font-family:var(--font-mono)}
+ .preview-info .infogrp .row.att{padding:6px 8px;margin:0 -8px;border-radius:6px;gap:10px}
+ .preview-info .infogrp .row.att:hover{background:var(--surface-2);color:var(--accent)}
.preview-info .row span:last-child{color:var(--ink);font-variant-numeric:tabular-nums}
.preview-info .actions{display:flex;flex-direction:column;gap:6px;margin-top:auto}
@@ -651,6 +642,8 @@ const FIcon = ({ type, ext, size = 28 }) => (
{ext}
);
+const TYPE_ICON = { doc: 'file', img: 'image', vid: 'video', zip: 'folder', code: 'code' };
+
const Thumb = ({ file }) => {
const k = file.thumb;
if (k === "doc") return ;
@@ -759,7 +752,7 @@ const Sidebar = ({ activeFolder, setActiveFolder, activeCat, setActiveCat, categ
);
};
-const Header = ({ folder, category, onUpload, accent, categories, currentUser, onLogout, onMenuToggle }) => {
+const Header = ({ folder, category, categories, currentUser, onLogout, onMenuToggle }) => {
const folderObj = FOLDERS_DEF.find(f => f.id === folder);
const catObj = categories.find(c => c.id === category);
return (
@@ -785,9 +778,6 @@ const Header = ({ folder, category, onUpload, accent, categories, currentUser, o
관리자
-
>
)}
@@ -878,7 +868,7 @@ const GridView = ({ files, selected, toggleSel, toggleStar, onOpen, isLoggedIn }
);
-const ListView = ({ files, selected, toggleSel, toggleStar, onOpen, categories, isLoggedIn, currentUser, onDownload, onCopyLink, onDelete }) => {
+const ListView = ({ files, selected, toggleSel, toggleStar, onOpen, categories, isLoggedIn, currentUser, onDownload, onDownloadAttachment, onCopyLink, onDelete }) => {
const [menuFile, setMenuFile] = useState(null);
const [menuPos, setMenuPos] = useState({ x: 0, y: 0 });
const menuRef = useRef(null);
@@ -902,8 +892,8 @@ const ListView = ({ files, selected, toggleSel, toggleStar, onOpen, categories,
};
const cols = isLoggedIn
- ? "32px 1fr 100px 130px 130px 70px 52px"
- : "1fr 100px 130px 130px 70px 52px";
+ ? "32px 1fr 100px 130px 130px 118px 52px"
+ : "1fr 100px 130px 130px 118px 52px";
return (
@@ -931,7 +921,15 @@ const ListView = ({ files, selected, toggleSel, toggleStar, onOpen, categories,
{f.size_label}
{f.modified_at}
{f.author}
- ↓ {f.downloads}
+ e.stopPropagation()}>
+ {(f.attachments || []).map(a => (
+
+ ))}
+ {(!f.attachments || f.attachments.length === 0) && ↓ {f.downloads}}
+
|