From d11ad03e4b76955e57333bcdbdb05ee985860866 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath <2+defunkt@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:22:31 -0700 Subject: [PATCH] extract js/css --- packages/cubby/main | 0 packages/cubby/public/css/main.css | 104 ++++++++ packages/cubby/public/js/project.js | 114 +++++++++ packages/cubby/src/components/Layout.tsx | 24 ++ packages/cubby/src/components/Project.tsx | 52 ++++ packages/cubby/src/server.tsx | 294 +--------------------- 6 files changed, 298 insertions(+), 290 deletions(-) create mode 100644 packages/cubby/main create mode 100644 packages/cubby/public/css/main.css create mode 100644 packages/cubby/public/js/project.js create mode 100644 packages/cubby/src/components/Layout.tsx create mode 100644 packages/cubby/src/components/Project.tsx diff --git a/packages/cubby/main b/packages/cubby/main new file mode 100644 index 0000000..e69de29 diff --git a/packages/cubby/public/css/main.css b/packages/cubby/public/css/main.css new file mode 100644 index 0000000..4386c20 --- /dev/null +++ b/packages/cubby/public/css/main.css @@ -0,0 +1,104 @@ +.content-wrapper { + display: flex; + gap: 2rem; +} + +.main-content { + flex: 1; + min-width: 0; +} + +.side-content { + width: 30%; + min-width: 250px; +} + +@media (max-width: 768px) { + .content-wrapper { + flex-direction: column; + } + .side-content { + width: 100%; + } +} + +main ul.file-list { + list-style: none; + padding: 0; +} + +main ul.file-list li { + display: flex; + align-items: center; + gap: 4px; +} + +main ul.file-list li .delete-button { + display: none; + padding: 0 4px; + margin: 0; + background: none; + border: none; + font-weight: bold; + color: #ff4444; + transition: transform 0.2s; + cursor: pointer; + font-size: 1.2em; + line-height: 1; +} + +main ul.file-list li:hover .delete-button { + display: block; +} + +main ul.file-list li .delete-button:hover { + transform: scale(1.2); +} + +.upload-container { + display: flex; + flex-direction: column; + gap: 8px; +} + +.file-input { + margin: 0; +} + +.upload-button { + width: 100%; +} + +.progress-container { + position: fixed; + bottom: 0; + left: 0; + right: 0; + padding: 10px; + background: rgba(0, 0, 0, 0.1); + display: none; +} + +.progress-bar { + width: 0%; + height: 20px; + background: #4CAF50; +} + +.drag-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.7); + z-index: 1000; + align-items: center; + justify-content: center; + display: none; +} + +.drag-text { + color: white; + font-size: 2em; +} \ No newline at end of file diff --git a/packages/cubby/public/js/project.js b/packages/cubby/public/js/project.js new file mode 100644 index 0000000..cac3604 --- /dev/null +++ b/packages/cubby/public/js/project.js @@ -0,0 +1,114 @@ +const fileInput = document.getElementById("file-input") +const uploadButton = document.getElementById("upload-button") +const progressContainer = document.getElementById("progress-container") +const progressBar = document.getElementById("progress-bar") +const progressText = document.getElementById("progress-text") +const dragOverlay = document.getElementById("drag-overlay") +const deleteButtons = document.querySelectorAll(".delete-button") +let dragCounter = 0 + +fileInput.addEventListener("change", () => { + uploadButton.style.display = fileInput.files.length > 0 ? "block" : "none" +}) + +deleteButtons.forEach(button => { + button.addEventListener("click", async () => { + const file = button.dataset.file + if (!confirm("Are you sure you want to delete " + file + "?")) return + + try { + const response = await fetch(window.location.pathname + "/delete/" + file, { + method: "DELETE" + }) + + if (response.ok) { + window.location.reload() + } else { + alert("Delete failed") + } + } catch (err) { + alert("Delete failed") + } + }) +}) + +function uploadFile(file) { + const xhr = new XMLHttpRequest() + const formData = new FormData() + formData.append("file", file) + + progressContainer.style.display = "block" + uploadButton.disabled = true + fileInput.disabled = true + + xhr.upload.addEventListener("progress", (e) => { + if (e.lengthComputable) { + const percent = Math.round((e.loaded / e.total) * 100) + progressBar.style.width = percent + "%" + progressText.textContent = percent + "%" + } + }) + + xhr.addEventListener("load", () => { + if (xhr.status === 200) { + window.location.reload() + } else { + alert("Upload failed") + progressContainer.style.display = "none" + uploadButton.disabled = false + fileInput.disabled = false + } + }) + + xhr.addEventListener("error", () => { + alert("Upload failed") + progressContainer.style.display = "none" + uploadButton.disabled = false + fileInput.disabled = false + }) + + xhr.open("POST", window.location.pathname + "/upload") + xhr.send(formData) +} + +uploadButton.addEventListener("click", () => { + const file = fileInput.files[0] + if (!file) return + uploadFile(file) +}) + +document.addEventListener("dragenter", (e) => { + e.preventDefault() + e.stopPropagation() + dragCounter++ + dragOverlay.style.display = "flex" +}) + +document.addEventListener("dragleave", (e) => { + e.preventDefault() + e.stopPropagation() + dragCounter-- + if (dragCounter === 0) { + dragOverlay.style.display = "none" + } +}) + +document.addEventListener("dragover", (e) => { + e.preventDefault() + e.stopPropagation() +}) + +document.addEventListener("drop", async (e) => { + e.preventDefault() + e.stopPropagation() + dragCounter = 0 + dragOverlay.style.display = "none" + + const files = [...e.dataTransfer.files] + if (files.length === 0) return + + for (const file of files) { + uploadFile(file) + break + } +}) \ No newline at end of file diff --git a/packages/cubby/src/components/Layout.tsx b/packages/cubby/src/components/Layout.tsx new file mode 100644 index 0000000..2ca29c3 --- /dev/null +++ b/packages/cubby/src/components/Layout.tsx @@ -0,0 +1,24 @@ +interface LayoutProps { + children: any + title?: string +} + +export function Layout({ children, title }: LayoutProps) { + return ( + +
+