Add mail menu sidebar

This commit is contained in:
DonVoo 2026-07-12 22:09:32 +02:00
parent 4616d725f2
commit c26389e65f
3 changed files with 104 additions and 8 deletions

View file

@ -54,13 +54,14 @@ func renderShell(w http.ResponseWriter, active, readingPane string) {
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-4">
<link rel="stylesheet" href="/static/style.css?v=20260712-6">
<script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260712-4" defer></script></head>
<script src="/static/app.js?v=20260712-6" defer></script></head>
<body class="ol2013">
<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>
<nav class="ribbon-tabs">
<a class="tab %s" href="/view"><span class="mail-logo" aria-hidden="true"></span><span>Archiv-Boxen</span></a>
<a class="tab %s" href="/view">Archiv-Boxen</a>
<a class="tab" href="/view">Import/Export</a>
<a class="tab" href="/accounts">Postfach-Verwaltung</a>
<a class="tab" href="/view">Mail-Transfer</a>
@ -68,6 +69,14 @@ func renderShell(w http.ResponseWriter, active, readingPane string) {
</nav>
<div class="ribbon-title">Mail-Graveyard</div>
</header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item active" href="/view">Archiv-Boxen</a>
<a class="backstage-item" href="/view">Import/Export</a>
<a class="backstage-item" href="/accounts">Postfach-Verwaltung</a>
<a class="backstage-item" href="/view">Mail-Transfer</a>
<a class="backstage-item" href="/accounts">Einstellungen</a>
</aside>
<button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button>
<div class="three-pane">
<aside class="pane pane-tree" id="tree"
hx-get="/view" hx-trigger="load" hx-target="#tree" hx-swap="innerHTML">
@ -275,13 +284,14 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account,
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Postfach-Verwaltung - Mail-Graveyard</title>
<link rel="stylesheet" href="/static/style.css?v=20260712-5">
<link rel="stylesheet" href="/static/style.css?v=20260712-6">
<script src="/static/htmx.min.js"></script>
<script src="/static/app.js?v=20260712-5" defer></script></head>
<script src="/static/app.js?v=20260712-6" defer></script></head>
<body class="ol2013">
<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>
<nav class="ribbon-tabs">
<a class="tab" href="/view"><span class="mail-logo" aria-hidden="true"></span><span>Archiv-Boxen</span></a>
<a class="tab" href="/view">Archiv-Boxen</a>
<a class="tab" href="/view">Import/Export</a>
<a class="tab active" href="/accounts">Postfach-Verwaltung</a>
<a class="tab" href="/view">Mail-Transfer</a>
@ -289,6 +299,14 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account,
</nav>
<div class="ribbon-title">Mail-Graveyard</div>
</header>
<aside class="backstage-menu" aria-label="Mail-Graveyard-Menue">
<a class="backstage-item" href="/view">Archiv-Boxen</a>
<a class="backstage-item" href="/view">Import/Export</a>
<a class="backstage-item active" href="/accounts">Postfach-Verwaltung</a>
<a class="backstage-item" href="/view">Mail-Transfer</a>
<a class="backstage-item" href="/accounts">Einstellungen</a>
</aside>
<button class="backstage-shade" type="button" aria-label="Menue schliessen" data-backstage-close></button>
<main class="accounts-page">`)
if msg != "" {
fmt.Fprintf(w, `<div class="notice ok">%s</div>`, html.EscapeString(msg))

View file

@ -93,6 +93,30 @@
window.addEventListener("DOMContentLoaded", () => bind(document.querySelector(".three-pane")));
})();
(function () {
function setOpen(open) {
document.body.classList.toggle("backstage-open", open);
document.querySelectorAll("[data-backstage-toggle]").forEach((button) => {
button.setAttribute("aria-expanded", open ? "true" : "false");
});
}
document.addEventListener("click", (event) => {
const toggle = event.target.closest("[data-backstage-toggle]");
if (toggle) {
setOpen(!document.body.classList.contains("backstage-open"));
return;
}
if (event.target.closest("[data-backstage-close]")) {
setOpen(false);
}
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") setOpen(false);
});
})();
(function () {
function rowPayload(row) {
return {

View file

@ -32,13 +32,28 @@ body.ol2013 {
color: #fff;
display: flex;
align-items: center;
gap: 24px;
padding: 0 16px;
gap: 12px;
padding: 0 16px 0 0;
height: 44px;
flex: 0 0 auto;
position: relative;
z-index: 20;
}
.ribbon-title { margin-left: auto; font-weight: 700; font-size: 15px; letter-spacing: .2px; }
.ribbon-tabs { display: flex; gap: 2px; height: 100%; }
.app-menu-button {
width: 54px;
height: 44px;
border: 0;
background: var(--ol-blue-dark);
color: #fff;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.app-menu-button:hover,
.app-menu-button[aria-expanded="true"] { background: #00365f; }
.tab {
display: flex; align-items: center;
gap: 8px;
@ -77,6 +92,45 @@ body.ol2013 {
transform-origin: right center;
}
.backstage-menu {
position: fixed;
top: 44px;
left: 0;
bottom: 22px;
width: 216px;
z-index: 18;
background: var(--ol-blue);
color: #fff;
padding: 10px 0;
transform: translateX(-100%);
transition: transform .16s ease-out;
box-shadow: 8px 0 18px rgba(0, 0, 0, .16);
}
.backstage-open .backstage-menu { transform: translateX(0); }
.backstage-item {
display: block;
padding: 11px 22px;
color: #fff;
text-decoration: none;
border-left: 3px solid transparent;
}
.backstage-item:hover { background: var(--ol-blue-dark); }
.backstage-item.active {
background: #fff;
color: var(--ol-blue);
border-left-color: #fff;
}
.backstage-shade {
position: fixed;
inset: 44px 0 22px 0;
z-index: 17;
border: 0;
background: transparent;
display: none;
cursor: default;
}
.backstage-open .backstage-shade { display: block; }
/* Viewer */
.three-pane,
.four-pane {