Use internal compose modal for mail actions
This commit is contained in:
parent
e8029a177c
commit
29fd3f5cae
3 changed files with 229 additions and 24 deletions
|
|
@ -61,6 +61,51 @@ func RegisterRoutes(mux *http.ServeMux) {
|
||||||
mux.HandleFunc("/view/target/select", targetSelectHandler)
|
mux.HandleFunc("/view/target/select", targetSelectHandler)
|
||||||
mux.HandleFunc("/view/manual-copy", manualCopyHandler)
|
mux.HandleFunc("/view/manual-copy", manualCopyHandler)
|
||||||
mux.HandleFunc("/view/forward", forwardHandler)
|
mux.HandleFunc("/view/forward", forwardHandler)
|
||||||
|
mux.HandleFunc("/mail/send", mailSendHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mailSendHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
to := splitRecipients(r.FormValue("to"))
|
||||||
|
subject := strings.TrimSpace(r.FormValue("subject"))
|
||||||
|
body := r.FormValue("body")
|
||||||
|
if len(to) == 0 {
|
||||||
|
http.Error(w, "Bitte mindestens einen Empfaenger eintragen.", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if subject == "" {
|
||||||
|
subject = "(ohne Betreff)"
|
||||||
|
}
|
||||||
|
if err := SendPlainMail(to, subject, body); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
|
fmt.Fprint(w, "gesendet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitRecipients(value string) []string {
|
||||||
|
fields := strings.FieldsFunc(value, func(r rune) bool {
|
||||||
|
return r == ',' || r == ';' || r == '\n' || r == '\r' || r == '\t' || r == ' '
|
||||||
|
})
|
||||||
|
recipients := make([]string, 0, len(fields))
|
||||||
|
seen := make(map[string]bool, len(fields))
|
||||||
|
for _, field := range fields {
|
||||||
|
addr := strings.TrimSpace(field)
|
||||||
|
if addr == "" || seen[strings.ToLower(addr)] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
recipients = append(recipients, addr)
|
||||||
|
seen[strings.ToLower(addr)] = true
|
||||||
|
}
|
||||||
|
return recipients
|
||||||
}
|
}
|
||||||
|
|
||||||
func homeHandler(w http.ResponseWriter, r *http.Request) {
|
func homeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -73,8 +118,8 @@ func renderStartPage(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Mail-Graveyard</title>
|
<title>Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
|
<link rel="stylesheet" href="/static/style.css?v=20260713-11">
|
||||||
<script src="/static/app.js?v=20260713-10" defer></script></head>
|
<script src="/static/app.js?v=20260713-11" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -121,9 +166,9 @@ func renderShell(w http.ResponseWriter, r *http.Request, active, readingPane str
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Mail-Graveyard</title>
|
<title>Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
|
<link rel="stylesheet" href="/static/style.css?v=20260713-11">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260713-10" defer></script></head>
|
<script src="/static/app.js?v=20260713-11" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -697,9 +742,9 @@ func renderAccountsPage(w http.ResponseWriter, r *http.Request, accounts []Accou
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Konten-Verwaltung - Mail-Graveyard</title>
|
<title>Konten-Verwaltung - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
|
<link rel="stylesheet" href="/static/style.css?v=20260713-11">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260713-10" defer></script></head>
|
<script src="/static/app.js?v=20260713-11" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -743,9 +788,9 @@ func renderArchivesPage(w http.ResponseWriter, r *http.Request, archives []strin
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Archiv-mbox Verwaltung - Mail-Graveyard</title>
|
<title>Archiv-mbox Verwaltung - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
|
<link rel="stylesheet" href="/static/style.css?v=20260713-11">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260713-10" defer></script></head>
|
<script src="/static/app.js?v=20260713-11" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -791,9 +836,9 @@ func renderUsersPage(w http.ResponseWriter, r *http.Request, users []AppUser, ed
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Benutzerverwaltung - Mail-Graveyard</title>
|
<title>Benutzerverwaltung - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
|
<link rel="stylesheet" href="/static/style.css?v=20260713-11">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260713-10" defer></script></head>
|
<script src="/static/app.js?v=20260713-11" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -865,9 +910,9 @@ func renderTransferPage(w http.ResponseWriter, r *http.Request, mode transferMod
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>%s - Mail-Graveyard</title>
|
<title>%s - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
|
<link rel="stylesheet" href="/static/style.css?v=20260713-11">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260713-10" defer></script></head>
|
<script src="/static/app.js?v=20260713-11" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -1317,9 +1362,9 @@ func renderSourceEmailBoxPage(w http.ResponseWriter, r *http.Request, accounts [
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Quell-Postfach - Mail-Graveyard</title>
|
<title>Quell-Postfach - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
|
<link rel="stylesheet" href="/static/style.css?v=20260713-11">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260713-10" defer></script></head>
|
<script src="/static/app.js?v=20260713-11" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -1361,9 +1406,9 @@ func renderTargetEmailBoxPage(w http.ResponseWriter, r *http.Request, accounts [
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Ziel-Postfach - Mail-Graveyard</title>
|
<title>Ziel-Postfach - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260713-10">
|
<link rel="stylesheet" href="/static/style.css?v=20260713-11">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260713-10" defer></script></head>
|
<script src="/static/app.js?v=20260713-11" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
|
||||||
85
frontend-js/dist/app.js
vendored
85
frontend-js/dist/app.js
vendored
|
|
@ -42,11 +42,49 @@
|
||||||
return `${prefix}: ${clean || "(ohne Betreff)"}`;
|
return `${prefix}: ${clean || "(ohne Betreff)"}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openMailto(mail) {
|
function ensureComposeModal() {
|
||||||
const params = new URLSearchParams();
|
let modal = document.getElementById("mailComposeModal");
|
||||||
if (mail.subject) params.set("subject", mail.subject);
|
if (modal) return modal;
|
||||||
if (mail.body) params.set("body", mail.body);
|
modal = document.createElement("div");
|
||||||
window.location.href = `mailto:${encodeURIComponent(mail.to || "")}?${params.toString()}`;
|
modal.className = "mail-compose-modal";
|
||||||
|
modal.id = "mailComposeModal";
|
||||||
|
modal.setAttribute("role", "dialog");
|
||||||
|
modal.setAttribute("aria-modal", "true");
|
||||||
|
modal.setAttribute("aria-label", "Mail verfassen");
|
||||||
|
modal.innerHTML = `
|
||||||
|
<form class="mail-compose-box">
|
||||||
|
<div class="mail-compose-head">
|
||||||
|
<h2>Mail verfassen</h2>
|
||||||
|
<button class="mail-compose-close" type="button" aria-label="Schliessen" data-compose-close>×</button>
|
||||||
|
</div>
|
||||||
|
<div class="mail-compose-fields">
|
||||||
|
<label>An<input name="to" type="text" autocomplete="email" required placeholder="empfaenger@example.com"></label>
|
||||||
|
<label>Betreff<input name="subject" type="text" placeholder="Betreff"></label>
|
||||||
|
<label>Nachricht<textarea name="body" rows="14"></textarea></label>
|
||||||
|
</div>
|
||||||
|
<div class="mail-compose-status" aria-live="polite"></div>
|
||||||
|
<div class="mail-compose-actions">
|
||||||
|
<button class="btn secondary" type="button" data-compose-close>Abbrechen</button>
|
||||||
|
<button class="btn" type="submit">Senden</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
return modal;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeComposeModal() {
|
||||||
|
document.getElementById("mailComposeModal")?.classList.remove("open");
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCompose(mail) {
|
||||||
|
const modal = ensureComposeModal();
|
||||||
|
modal.querySelector("[name='to']").value = mail.to || "";
|
||||||
|
modal.querySelector("[name='subject']").value = mail.subject || "";
|
||||||
|
modal.querySelector("[name='body']").value = mail.body || "";
|
||||||
|
modal.querySelector(".mail-compose-status").textContent = "";
|
||||||
|
modal.classList.add("open");
|
||||||
|
setTimeout(() => modal.querySelector("[name='to']").focus(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function quoteBody(message) {
|
function quoteBody(message) {
|
||||||
|
|
@ -85,7 +123,7 @@
|
||||||
});
|
});
|
||||||
document.addEventListener("click", (event) => {
|
document.addEventListener("click", (event) => {
|
||||||
if (event.target.closest("[data-action-new]")) {
|
if (event.target.closest("[data-action-new]")) {
|
||||||
openMailto({ to: "", subject: "", body: "" });
|
openCompose({ to: "", subject: "", body: "" });
|
||||||
}
|
}
|
||||||
if (event.target.closest("[data-action-refresh]")) window.location.reload();
|
if (event.target.closest("[data-action-refresh]")) window.location.reload();
|
||||||
if (event.target.closest("[data-action-reply]")) {
|
if (event.target.closest("[data-action-reply]")) {
|
||||||
|
|
@ -94,7 +132,7 @@
|
||||||
window.alert("Bitte zuerst eine Nachricht waehlen.");
|
window.alert("Bitte zuerst eine Nachricht waehlen.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
openMailto({ to: message.from, subject: prefixedSubject("Re", message.subject), body: quoteBody(message) });
|
openCompose({ to: message.from, subject: prefixedSubject("Re", message.subject), body: quoteBody(message) });
|
||||||
}
|
}
|
||||||
if (event.target.closest("[data-action-forward]")) {
|
if (event.target.closest("[data-action-forward]")) {
|
||||||
const message = selectedPreview();
|
const message = selectedPreview();
|
||||||
|
|
@ -102,7 +140,38 @@
|
||||||
window.alert("Bitte zuerst eine Nachricht waehlen.");
|
window.alert("Bitte zuerst eine Nachricht waehlen.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
openMailto({ to: "", subject: prefixedSubject("Fw", message.subject), body: forwardBody(message) });
|
openCompose({ to: "", subject: prefixedSubject("Fw", message.subject), body: forwardBody(message) });
|
||||||
|
}
|
||||||
|
if (event.target.closest("[data-compose-close]")) closeComposeModal();
|
||||||
|
});
|
||||||
|
document.addEventListener("click", (event) => {
|
||||||
|
if (event.target.id === "mailComposeModal") closeComposeModal();
|
||||||
|
});
|
||||||
|
document.addEventListener("keydown", (event) => {
|
||||||
|
if (event.key === "Escape") closeComposeModal();
|
||||||
|
});
|
||||||
|
document.addEventListener("submit", async (event) => {
|
||||||
|
const form = event.target.closest(".mail-compose-box");
|
||||||
|
if (!form) return;
|
||||||
|
event.preventDefault();
|
||||||
|
const status = form.querySelector(".mail-compose-status");
|
||||||
|
const submit = form.querySelector("button[type='submit']");
|
||||||
|
status.textContent = "Sende...";
|
||||||
|
submit.disabled = true;
|
||||||
|
try {
|
||||||
|
const response = await fetch("/mail/send", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" },
|
||||||
|
body: new URLSearchParams(new FormData(form)).toString()
|
||||||
|
});
|
||||||
|
const text = await response.text();
|
||||||
|
if (!response.ok) throw new Error(text || "Senden fehlgeschlagen.");
|
||||||
|
status.textContent = "Gesendet.";
|
||||||
|
setTimeout(closeComposeModal, 700);
|
||||||
|
} catch (error) {
|
||||||
|
status.textContent = error.message || "Senden fehlgeschlagen.";
|
||||||
|
} finally {
|
||||||
|
submit.disabled = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
document.addEventListener("input", (event) => {
|
document.addEventListener("input", (event) => {
|
||||||
|
|
|
||||||
91
frontend-js/dist/style.css
vendored
91
frontend-js/dist/style.css
vendored
|
|
@ -540,6 +540,97 @@ a.tree-node { display: block; color: inherit; text-decoration: none; }
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 20px 24px;
|
padding: 20px 24px;
|
||||||
}
|
}
|
||||||
|
.mail-compose-modal {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 70;
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24px;
|
||||||
|
background: rgba(0, 0, 0, .32);
|
||||||
|
}
|
||||||
|
.mail-compose-modal.open { display: flex; }
|
||||||
|
.mail-compose-box {
|
||||||
|
width: min(760px, calc(100vw - 48px));
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid var(--ol-border);
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, .28);
|
||||||
|
}
|
||||||
|
.mail-compose-head {
|
||||||
|
height: 42px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 12px 0 16px;
|
||||||
|
color: #fff;
|
||||||
|
background: #2f343a;
|
||||||
|
}
|
||||||
|
.mail-compose-head h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.mail-compose-close {
|
||||||
|
margin-left: auto;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border: 0;
|
||||||
|
color: #fff;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.mail-compose-close:hover { background: #454b53; }
|
||||||
|
.mail-compose-fields {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.mail-compose-fields label {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 72px minmax(0, 1fr);
|
||||||
|
align-items: start;
|
||||||
|
gap: 10px;
|
||||||
|
color: var(--ol-muted);
|
||||||
|
}
|
||||||
|
.mail-compose-fields input,
|
||||||
|
.mail-compose-fields textarea {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #b8c4d0;
|
||||||
|
padding: 7px 8px;
|
||||||
|
color: var(--ol-text);
|
||||||
|
background: #fff;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
.mail-compose-fields textarea {
|
||||||
|
min-height: 260px;
|
||||||
|
resize: vertical;
|
||||||
|
font-family: Consolas, "Courier New", monospace;
|
||||||
|
}
|
||||||
|
.mail-compose-fields input:focus,
|
||||||
|
.mail-compose-fields textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--ol-blue);
|
||||||
|
}
|
||||||
|
.mail-compose-status {
|
||||||
|
min-height: 20px;
|
||||||
|
padding: 0 16px 8px 98px;
|
||||||
|
color: var(--ol-muted);
|
||||||
|
}
|
||||||
|
.mail-compose-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-top: 1px solid var(--ol-border);
|
||||||
|
background: #f7f9fb;
|
||||||
|
}
|
||||||
.ef-empty { color: var(--ol-muted); padding: 24px; }
|
.ef-empty { color: var(--ol-muted); padding: 24px; }
|
||||||
.audit-note { padding: 10px 16px; border-bottom: 1px solid var(--ol-border); font-size: 12px; }
|
.audit-note { padding: 10px 16px; border-bottom: 1px solid var(--ol-border); font-size: 12px; }
|
||||||
.drop-zone {
|
.drop-zone {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue