diff --git a/public/admin.css b/public/admin.css index 6a6a075..0d088ec 100644 --- a/public/admin.css +++ b/public/admin.css @@ -487,24 +487,44 @@ label { display: grid; gap: 6px; color: var(--muted); font-size: 13px; } background: white; border-top: 1px solid var(--line); } +.reply.dragging { + box-shadow: inset 0 0 0 2px var(--brand); + background: #edf8f3; +} .reply-composer { display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 10px; + grid-template-columns: minmax(0, 1fr) 44px 48px; + gap: 8px; + align-items: stretch; } -.admin-dropzone { - min-height: 38px; - border: 1px dashed var(--line); - border-radius: 8px; - padding: 8px 10px; - display: flex; - align-items: center; - color: var(--muted); - background: #f8fbf9; +.reply-composer textarea { min-height: 72px; resize: vertical; } +.reply-icon-button { + min-height: 72px; + width: 100%; + display: grid; + place-items: center; + border-radius: 6px; + padding: 0; cursor: pointer; } -.admin-dropzone.dragging { border-color: var(--brand); background: #edf8f3; color: var(--ink); } -.admin-dropzone input { display: none; } +.reply-icon-button svg { + width: 21px; + height: 21px; + fill: currentColor; +} +.attach-button { + border: 1px solid var(--line); + background: #f8fbf9; + color: var(--muted); +} +.attach-button:hover { background: #edf3ef; color: var(--ink); } +.attach-button input { display: none; } +.send-button { + border: 0; + background: var(--brand); + color: white; +} +.send-button svg { transform: translateX(1px); } .reply-attachments { display: flex; flex-wrap: wrap; diff --git a/public/admin.html b/public/admin.html index e201e27..fb050b3 100644 --- a/public/admin.html +++ b/public/admin.html @@ -58,12 +58,18 @@ diff --git a/public/admin.js b/public/admin.js index 8d7732e..4acb775 100644 --- a/public/admin.js +++ b/public/admin.js @@ -119,35 +119,39 @@ $("#replyForm textarea").addEventListener("input", debounce(() => { }, 600)); $("#replyForm input[name='attachments']").addEventListener("change", (event) => { - state.replyFiles = mergeImageFiles(state.replyFiles, [...event.target.files]); + addReplyFiles([...event.target.files]); event.target.value = ""; - renderReplyAttachments(); -}); - -$(".admin-dropzone").addEventListener("keydown", (event) => { - if (event.key === "Enter" || event.key === " ") { - event.preventDefault(); - $("#replyForm input[name='attachments']").click(); - } }); for (const eventName of ["dragenter", "dragover"]) { - $(".admin-dropzone").addEventListener(eventName, (event) => { + $(".conversation").addEventListener(eventName, (event) => { event.preventDefault(); - $(".admin-dropzone").classList.add("dragging"); + if (!state.activeId) return; + $("#replyForm").classList.add("dragging"); }); } for (const eventName of ["dragleave", "drop"]) { - $(".admin-dropzone").addEventListener(eventName, (event) => { + $(".conversation").addEventListener(eventName, (event) => { event.preventDefault(); - $(".admin-dropzone").classList.remove("dragging"); + $("#replyForm").classList.remove("dragging"); }); } -$(".admin-dropzone").addEventListener("drop", (event) => { - state.replyFiles = mergeImageFiles(state.replyFiles, [...event.dataTransfer.files]); - renderReplyAttachments(); +$(".conversation").addEventListener("drop", (event) => { + if (!state.activeId) return; + addReplyFiles([...event.dataTransfer.files]); +}); + +$(".conversation").addEventListener("paste", (event) => { + if (!state.activeId) return; + const files = [...event.clipboardData?.items || []] + .filter((item) => item.kind === "file") + .map((item) => item.getAsFile()) + .filter(Boolean); + if (!files.length) return; + event.preventDefault(); + addReplyFiles(files); }); $("#replyAttachments").addEventListener("click", (event) => { @@ -458,6 +462,11 @@ function renderReplyAttachments() { `).join(""); } +function addReplyFiles(files) { + state.replyFiles = mergeImageFiles(state.replyFiles, files); + renderReplyAttachments(); +} + function clearReplyComposer(form = $("#replyForm")) { const textarea = form?.querySelector("textarea[name='message']"); if (!textarea) return;