reorganize header menu
This commit is contained in:
@@ -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
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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
@@ -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}}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user