Add manual target drag and drop
This commit is contained in:
parent
ffa28f6182
commit
a4eee37ab9
4 changed files with 229 additions and 35 deletions
85
frontend-js/dist/app.js
vendored
85
frontend-js/dist/app.js
vendored
|
|
@ -1,6 +1,6 @@
|
|||
(function () {
|
||||
const key = "mail-graveyard-pane-widths";
|
||||
const minWidths = [160, 220, 260, 260];
|
||||
const key = "mail-graveyard-pane-widths-v2";
|
||||
const minWidths = [160, 220, 300];
|
||||
const handleWidth = 6;
|
||||
|
||||
function panes(container) {
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
function clampWidths(widths, total) {
|
||||
const next = widths.map((w, i) => Math.max(minWidths[i], Math.round(w || minWidths[i])));
|
||||
const max = Math.max(total - handleWidth * 3, minWidths.reduce((a, b) => a + b, 0));
|
||||
const max = Math.max(total - handleWidth * (next.length - 1), minWidths.reduce((a, b) => a + b, 0));
|
||||
const sum = next.reduce((a, b) => a + b, 0);
|
||||
if (sum > max) {
|
||||
let extra = sum - max;
|
||||
|
|
@ -31,14 +31,14 @@
|
|||
const total = container.getBoundingClientRect().width;
|
||||
const w = clampWidths(widths, total);
|
||||
container.style.gridTemplateColumns =
|
||||
`${w[0]}px ${handleWidth}px ${w[1]}px ${handleWidth}px ${w[2]}px ${handleWidth}px ${w[3]}px`;
|
||||
`${w[0]}px ${handleWidth}px ${w[1]}px ${handleWidth}px minmax(${w[2]}px, 1fr)`;
|
||||
localStorage.setItem(key, JSON.stringify(w));
|
||||
}
|
||||
|
||||
function restore(container) {
|
||||
try {
|
||||
const saved = JSON.parse(localStorage.getItem(key) || "null");
|
||||
if (Array.isArray(saved) && saved.length === 4) {
|
||||
if (Array.isArray(saved) && saved.length === 3) {
|
||||
applyWidths(container, saved);
|
||||
}
|
||||
} catch (_) {
|
||||
|
|
@ -90,4 +90,79 @@
|
|||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => bind(document.querySelector(".four-pane")));
|
||||
window.addEventListener("DOMContentLoaded", () => bind(document.querySelector(".three-pane")));
|
||||
})();
|
||||
|
||||
(function () {
|
||||
function rowPayload(row) {
|
||||
return {
|
||||
source: row.dataset.source || "",
|
||||
folder: row.dataset.folder || "",
|
||||
index: row.dataset.index || ""
|
||||
};
|
||||
}
|
||||
|
||||
document.addEventListener("dragstart", (event) => {
|
||||
const row = event.target.closest(".msg-row[draggable='true']");
|
||||
if (!row || !event.dataTransfer) return;
|
||||
event.dataTransfer.effectAllowed = "copy";
|
||||
event.dataTransfer.setData("application/json", JSON.stringify(rowPayload(row)));
|
||||
event.dataTransfer.setData("text/plain", `${row.dataset.source || ""}/${row.dataset.folder || ""}#${row.dataset.index || ""}`);
|
||||
row.classList.add("dragging");
|
||||
});
|
||||
|
||||
document.addEventListener("dragend", (event) => {
|
||||
const row = event.target.closest(".msg-row");
|
||||
if (row) row.classList.remove("dragging");
|
||||
});
|
||||
|
||||
document.addEventListener("dragover", (event) => {
|
||||
const zone = event.target.closest("[data-drop-target='manual-copy']");
|
||||
if (!zone) return;
|
||||
event.preventDefault();
|
||||
zone.classList.add("drag-over");
|
||||
if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
|
||||
});
|
||||
|
||||
document.addEventListener("dragleave", (event) => {
|
||||
const zone = event.target.closest("[data-drop-target='manual-copy']");
|
||||
if (zone) zone.classList.remove("drag-over");
|
||||
});
|
||||
|
||||
document.addEventListener("drop", async (event) => {
|
||||
const zone = event.target.closest("[data-drop-target='manual-copy']");
|
||||
if (!zone) return;
|
||||
event.preventDefault();
|
||||
zone.classList.remove("drag-over");
|
||||
|
||||
let payload = {};
|
||||
try {
|
||||
payload = JSON.parse(event.dataTransfer.getData("application/json") || "{}");
|
||||
} catch (_) {
|
||||
payload = {};
|
||||
}
|
||||
if (!payload.source) {
|
||||
payload = {
|
||||
source: zone.dataset.source || "",
|
||||
folder: zone.dataset.folder || "",
|
||||
index: zone.dataset.index || ""
|
||||
};
|
||||
}
|
||||
|
||||
const select = document.querySelector("#target-account-select");
|
||||
const form = new URLSearchParams();
|
||||
form.set("source", payload.source);
|
||||
form.set("folder", payload.folder);
|
||||
form.set("index", payload.index);
|
||||
form.set("target_account", select ? select.value : "");
|
||||
zone.textContent = "Kopiere...";
|
||||
|
||||
const response = await fetch("/view/manual-copy", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" },
|
||||
body: form.toString()
|
||||
});
|
||||
const html = await response.text();
|
||||
document.querySelector("#read").innerHTML = html;
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
30
frontend-js/dist/style.css
vendored
30
frontend-js/dist/style.css
vendored
|
|
@ -48,14 +48,14 @@ body.ol2013 {
|
|||
.tab:hover { background: var(--ol-blue-dark); color: #fff; }
|
||||
.tab.active { background: #fff; color: var(--ol-blue); border-bottom-color: #fff; }
|
||||
|
||||
/* Vier-Spalten-Viewer */
|
||||
/* Viewer */
|
||||
.three-pane,
|
||||
.four-pane {
|
||||
flex: 1 1 auto;
|
||||
display: grid;
|
||||
min-height: 0; /* erlaubt Scrollen in den Panes */
|
||||
}
|
||||
.three-pane { grid-template-columns: 220px 340px 1fr; }
|
||||
.three-pane { grid-template-columns: 220px 6px 340px 6px minmax(300px, 1fr); }
|
||||
.four-pane { grid-template-columns: 220px 6px 340px 6px minmax(280px, 1fr) 6px minmax(280px, 1fr); }
|
||||
.pane {
|
||||
border-right: 1px solid var(--ol-border);
|
||||
|
|
@ -84,7 +84,7 @@ body.resizing { cursor: col-resize; user-select: none; }
|
|||
}
|
||||
.target-head {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
|
@ -124,6 +124,7 @@ a.tree-node { display: block; color: inherit; text-decoration: none; }
|
|||
display: flex; flex-direction: column; gap: 2px;
|
||||
}
|
||||
.msg-row:hover { background: #eef6fd; }
|
||||
.msg-row.dragging { opacity: .55; }
|
||||
.msg-row.active { background: var(--ol-sel); border-left: 3px solid var(--ol-blue); padding-left: 9px; }
|
||||
.msg-from { font-weight: 600; }
|
||||
.msg-subject { color: var(--ol-text); }
|
||||
|
|
@ -133,6 +134,12 @@ a.tree-node { display: block; color: inherit; text-decoration: none; }
|
|||
|
||||
/* Lesebereich */
|
||||
.read-body { padding: 16px 20px; }
|
||||
.read-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
.read-head { border-bottom: 1px solid var(--ol-border); padding-bottom: 10px; margin-bottom: 12px; }
|
||||
.read-subject { font-size: 17px; font-weight: 600; margin: 0 0 6px; }
|
||||
.read-meta { color: var(--ol-muted); font-size: 12px; }
|
||||
|
|
@ -145,6 +152,19 @@ a.tree-node { display: block; color: inherit; text-decoration: none; }
|
|||
}
|
||||
.ef-empty { color: var(--ol-muted); padding: 24px; }
|
||||
.audit-note { padding: 10px 16px; border-bottom: 1px solid var(--ol-border); font-size: 12px; }
|
||||
.drop-zone {
|
||||
margin: 10px 16px 0;
|
||||
padding: 12px;
|
||||
border: 1px dashed var(--ol-sel-bd);
|
||||
background: #f6fbff;
|
||||
color: var(--ol-muted);
|
||||
text-align: center;
|
||||
}
|
||||
.drop-zone.drag-over {
|
||||
background: var(--ol-sel);
|
||||
color: var(--ol-text);
|
||||
border-color: var(--ol-blue);
|
||||
}
|
||||
|
||||
/* Buttons / Formulare (flach, 2013er) */
|
||||
.btn {
|
||||
|
|
@ -154,6 +174,7 @@ a.tree-node { display: block; color: inherit; text-decoration: none; }
|
|||
.btn:hover { background: var(--ol-blue-dark); }
|
||||
.btn.secondary { background: #e1e1e1; color: var(--ol-text); border: 1px solid var(--ol-border); }
|
||||
.btn.secondary:hover { background: #d5d5d5; }
|
||||
.btn.compact { padding: 3px 9px; }
|
||||
.input, select, textarea {
|
||||
border: 1px solid #abadb3; padding: 5px 7px; font: inherit; width: 100%;
|
||||
background: #fff;
|
||||
|
|
@ -172,6 +193,9 @@ a.tree-node { display: block; color: inherit; text-decoration: none; }
|
|||
.bad { color: #d13438; }
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.three-pane {
|
||||
grid-template-columns: 200px 6px 320px 6px minmax(300px, 1fr);
|
||||
}
|
||||
.four-pane {
|
||||
grid-template-columns: 200px 6px 320px 6px minmax(280px, 1fr) 6px minmax(280px, 1fr);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue