Rework app-shell layout

This commit is contained in:
2026-07-01 09:59:32 +02:00
parent 5d069683c4
commit 1055c110f4
9 changed files with 318 additions and 170 deletions
+16
View File
@@ -0,0 +1,16 @@
// Wires the header-right [ACTIONS] dropdown. The buttons inside keep calling
// the existing page/actions.js handlers (newPage / movePage / deletePage) and
// the ?edit link, so the N/E/M keyboard shortcuts continue to work whether or
// not the menu is open. Toggling reuses the generic wireDropdown mechanism
// (global-shortcuts.js), which also handles outside-click / Escape close.
(function () {
var trigger = document.querySelector('[data-action="page-actions"]');
if (!trigger || typeof wireDropdown !== 'function') return;
wireDropdown(trigger);
var menu = trigger.parentElement.querySelector('.dropdown-menu');
if (!menu) return;
// Keep aria-expanded in sync with the menu's open state.
trigger.addEventListener('click', function () {
trigger.setAttribute('aria-expanded', menu.classList.contains('is-open') ? 'true' : 'false');
});
})();
+19 -12
View File
@@ -1,5 +1,23 @@
{{define "headScripts"}}<script src="/_/page/actions.js"></script>{{end}}
{{define "headerActions"}}{{if .CanEdit}}
<span class="dropdown">
<button type="button" class="btn" data-action="page-actions" aria-haspopup="true" aria-expanded="false" title="Page actions">ACTIONS</button>
<div class="dropdown-menu align-right">
<button class="btn btn-block" onclick="newPage()" title="New page (N)">NEW PAGE</button>
<a class="btn btn-block" href="?edit" title="Edit page (E)">EDIT PAGE</a>
<button class="btn btn-block" data-companion-reveal hidden title="Reveal in file manager">REVEAL ON CLIENT</button>
{{if not .IsRoot}}
<button class="btn btn-block" onclick="movePage()" title="Move page (M)">MOVE PAGE</button>
{{end}}
{{if not .IsRoot}}
<button class="btn btn-block danger" onclick="deletePage()" title="Delete page">DELETE PAGE</button>
{{end}}
</div>
</span>
<script src="/_/page/header-actions.js"></script>
{{end}}{{end}}
{{define "content"}}
{{if .Content}}
<div class="content">{{.Content}}</div>
@@ -49,15 +67,4 @@
<script src="/_/page/sidebar-fab.js"></script>
{{end}}
{{define "sidebar"}}{{if .CanEdit}}<nav class="actions panel panel-sidebar">
<div class="panel-header">ACTIONS</div>
<button class="btn btn-block" onclick="newPage()" title="New page (N)">NEW PAGE</button>
<a class="btn btn-block" href="?edit" title="Edit page (E)">EDIT PAGE</a>
<button class="btn btn-block" data-companion-reveal hidden title="Reveal in file manager">REVEAL ON CLIENT</button>
{{if not .IsRoot}}
<button class="btn btn-block" onclick="movePage()" title="Move page (M)">MOVE PAGE</button>
{{end}}
{{if not .IsRoot}}
<button class="btn btn-block danger" onclick="deletePage()" title="Delete page">DELETE PAGE</button>
{{end}}
</nav>{{end}}{{if .SidebarWidget}}{{.SidebarWidget}}{{end}}{{end}}
{{define "sidebar"}}{{if .SidebarWidget}}{{.SidebarWidget}}{{end}}{{end}}
+9 -12
View File
@@ -1,23 +1,20 @@
// Mobile: the right-rail FAB is stacked ABOVE the tree FAB and opens the right
// rail (TOC + page widgets) in the full-viewport Overlay (overlay.js). It only
// appears when the rail actually has content — runs after toc.js has had a
// chance to populate the rail (script order in page/main.html), so an
// otherwise-empty rail shows no FAB. Hidden on desktop by .fab CSS.
document.addEventListener("DOMContentLoaded", function () {
var aside = document.querySelector("aside.sidebar");
if (!aside || !aside.children.length) return;
var fab = document.createElement("button");
fab.type = "button";
fab.className = "btn btn-fab fab";
fab.title = "Menu";
fab.setAttribute("aria-label", "Menu");
fab.setAttribute("aria-expanded", "false");
fab.className = "btn btn-fab fab fab-rail";
fab.title = "Contents";
fab.setAttribute("aria-label", "Contents");
fab.textContent = "≡";
fab.addEventListener("click", function () {
var open = aside.classList.toggle("is-open");
fab.setAttribute("aria-expanded", open ? "true" : "false");
});
aside.addEventListener("click", function (e) {
if (e.target.tagName === "A") {
aside.classList.remove("is-open");
fab.setAttribute("aria-expanded", "false");
}
if (typeof openOverlay === "function") openOverlay(aside);
});
document.body.appendChild(fab);
});
+2 -1
View File
@@ -28,6 +28,7 @@ document.addEventListener("DOMContentLoaded", function () {
});
nav.appendChild(list);
// Stack the TOC on top of any server-rendered widget(s) already in the rail.
var rail = document.querySelector("aside.sidebar");
rail.appendChild(nav);
rail.insertBefore(nav, rail.firstChild);
});