Inital attempt

This commit is contained in:
2026-05-15 11:26:07 +02:00
parent a9519f747e
commit 3e765aa54f
6 changed files with 134 additions and 76 deletions
+5 -14
View File
@@ -2,6 +2,8 @@
var cal = document.querySelector(".diary-cal"); var cal = document.querySelector(".diary-cal");
if (!cal) return; if (!cal) return;
cal.querySelectorAll(".dropdown > button").forEach(wireDropdown);
var toggle = document.createElement("button"); var toggle = document.createElement("button");
toggle.type = "button"; toggle.type = "button";
toggle.className = "panel-toggle"; toggle.className = "panel-toggle";
@@ -13,20 +15,9 @@
}); });
var main = document.querySelector("main"); var main = document.querySelector("main");
if (main) { if (!main) return;
main.parentNode.insertBefore(toggle, main); main.parentNode.insertBefore(toggle, main);
if (window.matchMedia("(max-width: 1100px)").matches) {
main.parentNode.insertBefore(cal, main); main.parentNode.insertBefore(cal, main);
} }
cal.querySelectorAll(".dropdown > button").forEach(wireDropdown);
var pageHeader = document.querySelector("header");
function updateTop() {
if (!pageHeader || getComputedStyle(cal).position !== "fixed") return;
var rect = pageHeader.getBoundingClientRect();
cal.style.top = Math.max(8, rect.bottom + 8) + "px";
}
window.addEventListener("scroll", updateTop, { passive: true });
window.addEventListener("resize", updateTop);
updateTop();
})(); })();
+23 -1
View File
@@ -125,5 +125,27 @@ function deletePage() {
} }
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
wireDropdown(document.querySelector('[data-action="actions-drop"]')); var panel = document.querySelector('.actions');
if (!panel) return;
var toggle = document.createElement('button');
toggle.type = 'button';
toggle.className = 'panel-toggle';
toggle.textContent = 'Actions';
toggle.setAttribute('aria-expanded', 'false');
toggle.addEventListener('click', function () {
var open = panel.classList.toggle('is-open');
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
});
var main = document.querySelector('main');
if (!main) return;
// Insert at the top of main's parent (right after the header) so the
// ACTIONS toggle sits above any toggles other panels may have added.
var header = document.querySelector('header');
var anchor = header ? header.nextSibling : main.parentNode.firstChild;
main.parentNode.insertBefore(toggle, anchor);
if (window.matchMedia('(max-width: 1100px)').matches) {
main.parentNode.insertBefore(panel, toggle.nextSibling);
}
}); });
-18
View File
@@ -1,18 +0,0 @@
// Lift the floating action button above the footer when the footer is on
// screen, so it never overlaps the request-time line or the companion icon.
// Mirrors the TOC's header-aware top-offset behaviour in toc.js.
(function () {
var fab = document.querySelector(".fab");
var footer = document.querySelector("footer");
if (!fab || !footer) return;
function updateBottom() {
var rect = footer.getBoundingClientRect();
var overlap = Math.max(0, window.innerHeight - rect.top);
fab.style.bottom = (overlap + 16) + "px";
}
window.addEventListener("scroll", updateBottom, { passive: true });
window.addEventListener("resize", updateBottom);
updateBottom();
})();
+12 -13
View File
@@ -35,20 +35,19 @@
{{end}} {{end}}
{{define "extras"}} {{define "extras"}}
{{if .SidebarWidget}}{{.SidebarWidget}}{{end}} <div class="right-rail">
{{if .CanEdit}} {{if .CanEdit}}
<script src="/_/page/fab.js" defer></script> <nav class="actions">
<div class="fab dropdown"> <div class="panel-header">ACTIONS</div>
<button class="btn btn-fab" data-action="actions-drop" title="Actions" aria-label="Actions"></button> <button class="btn" onclick="newPage()" title="New page (N)">NEW</button>
<div class="dropdown-menu align-right open-up"> <a class="btn" href="?edit" title="Edit page (E)">EDIT</a>
<button class="btn dropdown-item" onclick="newPage()" title="New page (N)">NEW</button> <button class="btn" data-companion-reveal hidden title="Reveal in file manager">REVEAL</button>
<a class="btn dropdown-item" href="?edit" title="Edit page (E)">EDIT</a>
<button class="btn dropdown-item" data-companion-reveal hidden title="Reveal in file manager">REVEAL</button>
{{if not .IsRoot}} {{if not .IsRoot}}
<button class="btn dropdown-item" onclick="movePage()" title="Move page (M)">MOVE</button> <button class="btn" onclick="movePage()" title="Move page (M)">MOVE</button>
<button class="btn dropdown-item danger" onclick="deletePage()" title="Delete page">DELETE</button> <button class="btn danger" onclick="deletePage()" title="Delete page">DELETE</button>
{{end}} {{end}}
</div> </nav>
{{end}}
{{if .SidebarWidget}}{{.SidebarWidget}}{{end}}
</div> </div>
{{end}} {{end}}
{{end}}
+20 -28
View File
@@ -1,4 +1,4 @@
(function () { document.addEventListener("DOMContentLoaded", function () {
var content = document.querySelector("main"); var content = document.querySelector("main");
if (!content) return; if (!content) return;
@@ -28,32 +28,24 @@
}); });
nav.appendChild(list); nav.appendChild(list);
var toggle = document.createElement("button"); var rail = document.querySelector(".right-rail");
toggle.type = "button"; if (!rail) {
toggle.className = "panel-toggle"; rail = document.createElement("div");
toggle.textContent = "Contents"; rail.className = "right-rail";
toggle.setAttribute("aria-expanded", "false"); document.body.appendChild(rail);
toggle.addEventListener("click", function () { }
rail.appendChild(nav);
var fab = document.createElement("button");
fab.type = "button";
fab.className = "btn btn-fab fab";
fab.title = "Contents";
fab.setAttribute("aria-label", "Contents");
fab.setAttribute("aria-expanded", "false");
fab.textContent = "≡";
fab.addEventListener("click", function () {
var open = nav.classList.toggle("is-open"); var open = nav.classList.toggle("is-open");
toggle.setAttribute("aria-expanded", open ? "true" : "false"); fab.setAttribute("aria-expanded", open ? "true" : "false");
}); });
document.body.appendChild(fab);
var main = document.querySelector("main"); });
if (main) {
main.parentNode.insertBefore(toggle, main);
main.parentNode.insertBefore(nav, main);
} else {
document.body.appendChild(toggle);
document.body.appendChild(nav);
}
var pageHeader = document.querySelector("header");
function updateTop() {
if (!pageHeader || getComputedStyle(nav).position !== "fixed") return;
var rect = pageHeader.getBoundingClientRect();
nav.style.top = Math.max(8, rect.bottom + 8) + "px";
}
window.addEventListener("scroll", updateTop, { passive: true });
window.addEventListener("resize", updateTop);
updateTop();
})();
+74 -2
View File
@@ -348,6 +348,12 @@ main > h2 {
z-index: 50; z-index: 50;
} }
/* Standalone FAB buttons (page TOC) are mobile-only.
Wrapped FABs (search actions dropdown) stay visible on desktop. */
button.fab {
display: none;
}
.btn-fab { .btn-fab {
background: var(--bg-panel); background: var(--bg-panel);
border: 1px solid var(--secondary); border: 1px solid var(--secondary);
@@ -540,6 +546,43 @@ hr {
background: var(--primary-hover); background: var(--primary-hover);
} }
/* === Right rail === */
.right-rail {
position: fixed;
top: 1rem;
right: 1rem;
width: 14rem;
max-height: calc(100vh - 2rem);
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1rem;
z-index: 10;
}
/* Panels share visual treatment when inside the rail */
.actions,
.right-rail .toc,
.right-rail .diary-cal {
position: static;
top: auto;
left: auto;
right: auto;
width: auto;
max-height: none;
border: 1px solid var(--secondary);
background: var(--bg);
padding: 0.5rem 0.75rem;
font-size: 0.85rem;
}
.actions {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.15rem;
}
/* === Table of contents === */ /* === Table of contents === */
.toc { .toc {
position: fixed; position: fixed;
@@ -832,6 +875,16 @@ hr {
/* === Responsive === */ /* === Responsive === */
@media (max-width: 1100px) { @media (max-width: 1100px) {
.right-rail {
position: static;
width: auto;
max-height: none;
overflow: visible;
display: contents;
}
button.fab {
display: inline-flex;
}
.panel-toggle { .panel-toggle {
display: block; display: block;
background: none; background: none;
@@ -854,7 +907,7 @@ hr {
.panel-toggle[aria-expanded="true"]::before { .panel-toggle[aria-expanded="true"]::before {
content: "▾ "; content: "▾ ";
} }
.toc, .actions,
.diary-cal { .diary-cal {
position: static; position: static;
display: none; display: none;
@@ -863,10 +916,29 @@ hr {
margin: 0 auto 1rem; margin: 0 auto 1rem;
max-height: none; max-height: none;
} }
.toc.is-open, .actions.is-open {
display: flex;
}
.diary-cal.is-open { .diary-cal.is-open {
display: block; display: block;
} }
/* TOC on mobile is a floating overlay toggled by the FAB. */
.toc {
position: fixed;
bottom: 5rem;
right: 1rem;
top: auto;
left: auto;
width: calc(100% - 2rem);
max-width: 20rem;
max-height: calc(100vh - 8rem);
overflow-y: auto;
display: none;
z-index: 60;
}
.toc.is-open {
display: block;
}
} }
@media (max-width: 600px) { @media (max-width: 600px) {