reorganize header menu

This commit is contained in:
2026-09-01 19:44:08 +02:00
parent 5ad715426d
commit 96a58ca472
10 changed files with 73 additions and 97 deletions
+11
View File
@@ -56,3 +56,14 @@ document.addEventListener('keydown', function (e) {
menu.classList.remove('is-open');
});
});
document.addEventListener('DOMContentLoaded', function () {
var trigger = document.querySelector('[data-action="actions-menu"]');
if (!trigger) return;
wireDropdown(trigger);
var menu = trigger.parentElement.querySelector('.dropdown-menu');
if (!menu) return;
trigger.addEventListener('click', function () {
trigger.setAttribute('aria-expanded', menu.classList.contains('is-open') ? true : false);
});
});
+11 -1
View File
@@ -29,7 +29,17 @@
<input class="input search-input" type="search" name="q" value="{{block "searchQuery" .}}{{end}}" placeholder="Search…" title="Search (F)" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
</form>
{{end}}
<div class="header-actions row">{{block "headerActions" .}}{{end}}</div>
<div class="header-actions row">
{{if .EditMode}}{{block "headerActions" .}}{{end}}{{else}}
<span class="dropdown actions">
<button type="button" class="btn actions-toggle" data-action="actions-menu" aria-haspopup="true" aria-expanded="false" title="Actions">ACTIONS</button>
<div class="dropdown-menu align-right actions-menu">
<div class="actions-special"></div>
{{template "headerActions" . }}
</div>
</span>
{{end}}
</div>
</header>
<div class="shell">
{{if not .EditMode}}<aside class="tree-sidebar col">
+15
View File
@@ -0,0 +1,15 @@
document.addEventListener("DOMContentLoaded", function () {
var aside = document.querySelector("aside.sidebar");
var specials = document.querySelector(".actions-special");
if (!aside || !aside.children.length || !specials) return;
var item = document.createElement("button");
item.type = "button";
item.className = "btn btn-block";
item.title = "Contents";
item.textContent = "CONTENTS";
item.addEventListener("click", function () {
if (typeof openOverlay === "function") openOverlay(aside);
});
specials.appendChild(item);
});
-16
View File
@@ -1,16 +0,0 @@
// 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');
});
})();
+6 -14
View File
@@ -1,21 +1,13 @@
{{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>
<button class="btn btn-block" onclick="newPage()" title="New page (N)">NEW</button>
<a class="btn btn-block" href="?edit" title="Edit page (E)">EDIT</a>
<button class="btn btn-block" data-companion-reveal hidden title="Reveal in file manager">REVEAL</button>
{{if not .IsRoot}}
<button class="btn btn-block" onclick="movePage()" title="Move page (M)">MOVE PAGE</button>
<button class="btn btn-block" onclick="movePage()" title="Move page (M)">MOVE</button>
<button class="btn btn-block danger" onclick="deletePage()" title="Delete page">DELETE</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"}}
@@ -63,7 +55,7 @@
{{if .Content}}
<script src="/_/page/sections.js"></script>
{{end}}
<script src="/_/page/sidebar-fab.js"></script>
<script src="/_/page/contents.js"></script>
{{end}}
{{define "aside"}}{{if .SidebarWidget}}<aside class="sidebar">{{.SidebarWidget}}</aside>{{end}}{{end}}
-15
View File
@@ -1,15 +0,0 @@
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-rail";
fab.title = "Contents";
fab.setAttribute("aria-label", "Contents");
fab.textContent = "≡";
fab.addEventListener("click", function () {
if (typeof openOverlay === "function") openOverlay(aside);
});
document.body.appendChild(fab);
});
-1
View File
@@ -53,7 +53,6 @@ function createSearchPage(name) {
}
document.addEventListener('DOMContentLoaded', function () {
wireDropdown(document.querySelector('[data-action="actions-drop"]'));
var createLink = document.querySelector('[data-create-page]');
if (createLink) {
+4 -9
View File
@@ -42,15 +42,10 @@
{{end}}
{{end}}
{{define "headerActions"}}
<button class="btn btn-block" onclick="rebuildIndex()" title="Rebuild search index">REBUILD INDEX</button>
{{end}}
{{define "footerExtras"}}
{{if not .IndexBuiltAt.IsZero}}<span class="muted">· Index: {{.IndexBuiltAt.Format "2006-01-02 15:04"}}</span>{{end}}
{{end}}
{{define "extras"}}
<div class="fab dropdown">
<button class="btn btn-fab" data-action="actions-drop" title="Actions" aria-label="Actions">≡</button>
<div class="dropdown-menu align-right open-up">
<button class="btn btn-block" onclick="rebuildIndex()" title="Rebuild search index">REBUILD INDEX</button>
</div>
</div>
{{end}}
+10 -14
View File
@@ -230,6 +230,7 @@ footer {
}
.btn-fab::before, .btn-fab::after { content: none; }
.btn-fab:hover { background: var(--bg-panel-hover); color: var(--primary-hover); }
.btn[hidden] {display: none;}
.danger { color: var(--danger); }
.danger:hover { color: var(--danger-hover); }
/* Selected segmented-toggle button (view-settings modal). */
@@ -403,6 +404,8 @@ main > h2 {
.dropdown-menu.is-open { display: block; }
.dropdown-menu.scrollable { max-height: 23rem; overflow-y: auto; }
.actions-special > .btn {text-align: left;}
/* === Suggestion dropdown (header search + editor link picker) ===
Anchored to a position:relative host. Mirrors .dropdown-menu visuals with
a dashed border and bg-panel-hover for the active row. */
@@ -501,15 +504,6 @@ body.edit-mode footer { max-width: none; }
border-bottom: var(--border-dashed);
}
/* === Floating action button ===
Standalone FAB buttons (the tree rail, the right rail) are mobile-only and
stack bottom-right: the tree FAB anchors the bottom, the right-rail FAB sits
above it. Wrapped FABs (the search actions dropdown) stay visible on desktop
and, on mobile, sit in the upper slot so they never overlap the tree FAB. */
.fab { position: fixed; bottom: var(--space-4); right: var(--space-4); z-index: 50; }
button.fab { display: none; }
.fab-rail { bottom: calc(var(--space-4) + 3rem + var(--space-2)); }
/* === Companion status === */
.companion-status { margin-left: auto; }
.companion-icon { font-size: 0.9rem; line-height: 1; padding: 0.1rem 0.3rem; }
@@ -860,11 +854,13 @@ aside.sidebar:empty { display: none; }
actions right. */
header { grid-template-columns: auto 1fr auto; }
.search-form { width: 100%; max-width: none; justify-self: stretch; }
/* Reveal the mobile FAB stack (tree at the bottom, right rail above it).
The search actions dropdown FAB moves to the upper slot too so it never
overlaps the tree FAB. */
button.fab { display: inline-flex; }
.fab.dropdown { bottom: calc(var(--space-4) + 3rem + var(--space-2)); }
}
/* Show the actions menu items directly in the bar in desktop size */
@media (min-width: 1101px) {
.actions-toggle, .actions-special {display: none;}
.actions, .actions-menu, .actions.menu.is-open {display: contents;}
.actions-menu .btn-block { width: auto; padding: 0;}
}
@media (max-width: 600px) {
+13 -24
View File
@@ -1,11 +1,3 @@
// Persistent left navigation rail. Renders the wiki folder/file tree, expanded
// to the current page's ancestor chain, and lets the user navigate by clicking
// folders (links) or open files locally via the companion (a.tree-file, wired
// in companion.js). Shares the .tree-* CSS and the ?tree endpoint with
// tree-picker.js but does not reuse its row builder — that one is modal-select
// behavior, this one is navigation behavior. Hidden in edit mode (the server
// omits the container).
(function () {
// This script owns the .tree-scroll child of the aside, not the aside itself.
var container = document.querySelector('aside.tree-sidebar .tree-scroll');
@@ -188,24 +180,21 @@
});
}
// Mobile: the tree FAB sits at the bottom of the stacked FAB group and
// opens the tree's scroll container in the full-viewport Overlay
// (overlay.js). The container always exists when not editing, so — per the
// layout spec — the FAB always renders; the overlay auto-closes when a
// folder/file link inside it navigates. Hidden on desktop by .fab CSS.
function setupFab() {
var fab = document.createElement('button');
fab.type = 'button';
fab.className = 'btn btn-fab fab fab-tree';
fab.title = 'Folder tree';
fab.setAttribute('aria-label', 'Folder tree');
fab.innerHTML = '<svg viewBox="0 0 16 16" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="miter"><path d="M1 6h14v8H1zm0 0V4h5l1 2"/></svg>';
fab.addEventListener('click', function () {
if (typeof openOverlay === 'function') openOverlay(container);
function setupMenuItem() {
var specials = document.querySelector('.actions-special');
if (!specials) return;
var item = document.createElement("button");
item.type = "button";
item.className = "btn btn-block";
item.title = "Folders";
item.textContent = "FOLDERS";
item.addEventListener("click", function () {
if (typeof openOverlay === "function") openOverlay(aside);
});
document.body.appendChild(fab);
specials.appendChild(item);
}
render();
setupFab();
setupMenuItem();
})();