Compare commits

...
3 Commits
Author SHA1 Message Date
luxick c0b7ebf0c2 Increase dropdown button on mobile 2026-09-01 20:05:23 +02:00
luxick 07bb3b002e Remove remaining mentions of fabs 2026-09-01 19:55:40 +02:00
luxick 96a58ca472 reorganize header menu 2026-09-01 19:44:08 +02:00
11 changed files with 91 additions and 130 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">
+1 -6
View File
@@ -26,7 +26,7 @@
closeBtn = document.createElement('button');
closeBtn.type = 'button';
closeBtn.className = 'btn btn-fab overlay-close';
closeBtn.className = 'btn overlay-close';
closeBtn.title = 'Close';
closeBtn.setAttribute('aria-label', 'Close');
closeBtn.textContent = '×';
@@ -35,11 +35,6 @@
bodyEl = document.createElement('div');
bodyEl.className = 'overlay-body';
// Any navigation or anchor activation inside a rail dismisses the
// overlay: tree/calendar links navigate away, TOC links scroll the
// page underneath — either way the user should see the result, and
// closing synchronously (before navigation) avoids a bfcache snapshot
// that would restore the overlay open on back-nav.
bodyEl.addEventListener('click', function (e) {
if (e.target.closest('a')) close();
});
+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');
});
})();
+8 -16
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>
{{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>
<button class="btn btn-block" onclick="newPage()" title="New page (N)">NEW</button>
<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</button>
<button class="btn btn-block danger" onclick="deletePage()" title="Delete page">DELETE</button>
{{end}}
<a class="btn btn-block" href="?edit" title="Edit page (E)">EDIT</a>
{{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}}
+27 -41
View File
@@ -217,22 +217,9 @@ footer {
padding: 0.3rem var(--space-3);
white-space: nowrap;
}
.btn-fab {
background: var(--bg-panel);
border: var(--border);
width: 3rem;
height: 3rem;
font-size: 1.5rem;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
}
.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). */
.btn.is-active { color: var(--primary-hover); }
/* === Form controls ===
@@ -403,6 +390,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 +490,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; }
@@ -691,7 +671,13 @@ aside.sidebar:empty { display: none; }
border-bottom: var(--border-dashed);
flex-shrink: 0;
}
.overlay-close { width: 2.5rem; height: 2.5rem; font-size: 1.5rem; }
.overlay-close {
width: 2.5rem;
height: 2.5rem;
font-size: 1.5rem;
}
.overlay-close::before, .overlay-close::after { content: none; }
.overlay-close:hover { background: var(--bg-panel-hover); color: var(--primary-hover); }
.overlay-body {
flex: 1;
overflow-y: auto;
@@ -755,12 +741,7 @@ aside.sidebar:empty { display: none; }
}
.tree-row.is-active > .tree-name { color: var(--primary-hover); }
/* === Tree sidebar (persistent left navigation rail) ===
Reuses the .tree-row / .tree-children / .tree-name / .tree-chevron modules.
Desktop: a full-height flex column in the app-shell (composes with .col)
holding the tree's own scroll region. Mobile: not laid out inline — its
content is surfaced through the Overlay via the stacked FABs (see
responsive). */
/* Tree sidebar (persistent navigation) */
.tree-sidebar {
width: var(--tree-width);
flex-shrink: 0;
@@ -851,20 +832,25 @@ aside.sidebar:empty { display: none; }
/* === Responsive === */
@media (max-width: 1100px) {
/* Rails are not laid out inline on mobile — their content is surfaced
through the Overlay via the stacked FABs. The <aside> elements stay in
the DOM (their JS renders into them and the Overlay moves them in/out);
we only pull them out of the shell flow here. */
.tree-sidebar, .sidebar { display: none; }
/* Compact three-column header: logo left, search stretches the middle,
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)); }
.actions-menu .btn-block {
min-height: 2.75rem;
padding: 0.8rem var(--space-4);
border-top: var(--border-dashed);
}
.actions-menu > .btn-block:first-child,
.actions-special > .btn-block:first-child { border-top: none; }
.actions-menu .btn-block:hover,
.actions-menu .btn-block:active { background: var(--bg-panel-hover); }
}
/* 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.dropdown-menu.is-open {display: contents;}
.actions-menu .btn-block { width: auto; padding: 0;}
}
@media (max-width: 600px) {
+14 -25
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);
});
document.body.appendChild(fab);
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(container);
});
specials.appendChild(item);
}
render();
setupFab();
setupMenuItem();
})();