reorganize header menu
This commit is contained in:
@@ -56,3 +56,14 @@ document.addEventListener('keydown', function (e) {
|
|||||||
menu.classList.remove('is-open');
|
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" />
|
<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>
|
</form>
|
||||||
{{end}}
|
{{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>
|
</header>
|
||||||
<div class="shell">
|
<div class="shell">
|
||||||
{{if not .EditMode}}<aside class="tree-sidebar col">
|
{{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');
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
+8
-16
@@ -1,21 +1,13 @@
|
|||||||
{{define "headScripts"}}<script src="/_/page/actions.js"></script>{{end}}
|
{{define "headScripts"}}<script src="/_/page/actions.js"></script>{{end}}
|
||||||
|
|
||||||
{{define "headerActions"}}{{if .CanEdit}}
|
{{define "headerActions"}}{{if .CanEdit}}
|
||||||
<span class="dropdown">
|
<button class="btn btn-block" onclick="newPage()" title="New page (N)">NEW</button>
|
||||||
<button type="button" class="btn" data-action="page-actions" aria-haspopup="true" aria-expanded="false" title="Page actions">ACTIONS</button>
|
<a class="btn btn-block" href="?edit" title="Edit page (E)">EDIT</a>
|
||||||
<div class="dropdown-menu align-right">
|
<button class="btn btn-block" data-companion-reveal hidden title="Reveal in file manager">REVEAL</button>
|
||||||
<button class="btn btn-block" onclick="newPage()" title="New page (N)">NEW PAGE</button>
|
{{if not .IsRoot}}
|
||||||
<a class="btn btn-block" href="?edit" title="Edit page (E)">EDIT PAGE</a>
|
<button class="btn btn-block" onclick="movePage()" title="Move page (M)">MOVE</button>
|
||||||
<button class="btn btn-block" data-companion-reveal hidden title="Reveal in file manager">REVEAL ON CLIENT</button>
|
<button class="btn btn-block danger" onclick="deletePage()" title="Delete page">DELETE</button>
|
||||||
{{if not .IsRoot}}
|
{{end}}
|
||||||
<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}}
|
{{end}}{{end}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
@@ -63,7 +55,7 @@
|
|||||||
{{if .Content}}
|
{{if .Content}}
|
||||||
<script src="/_/page/sections.js"></script>
|
<script src="/_/page/sections.js"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
<script src="/_/page/sidebar-fab.js"></script>
|
<script src="/_/page/contents.js"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "aside"}}{{if .SidebarWidget}}<aside class="sidebar">{{.SidebarWidget}}</aside>{{end}}{{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 () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
wireDropdown(document.querySelector('[data-action="actions-drop"]'));
|
|
||||||
|
|
||||||
var createLink = document.querySelector('[data-create-page]');
|
var createLink = document.querySelector('[data-create-page]');
|
||||||
if (createLink) {
|
if (createLink) {
|
||||||
|
|||||||
@@ -42,15 +42,10 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{define "headerActions"}}
|
||||||
|
<button class="btn btn-block" onclick="rebuildIndex()" title="Rebuild search index">REBUILD INDEX</button>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{define "footerExtras"}}
|
{{define "footerExtras"}}
|
||||||
{{if not .IndexBuiltAt.IsZero}}<span class="muted">· Index: {{.IndexBuiltAt.Format "2006-01-02 15:04"}}</span>{{end}}
|
{{if not .IndexBuiltAt.IsZero}}<span class="muted">· Index: {{.IndexBuiltAt.Format "2006-01-02 15:04"}}</span>{{end}}
|
||||||
{{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::before, .btn-fab::after { content: none; }
|
||||||
.btn-fab:hover { background: var(--bg-panel-hover); color: var(--primary-hover); }
|
.btn-fab:hover { background: var(--bg-panel-hover); color: var(--primary-hover); }
|
||||||
|
.btn[hidden] {display: none;}
|
||||||
.danger { color: var(--danger); }
|
.danger { color: var(--danger); }
|
||||||
.danger:hover { color: var(--danger-hover); }
|
.danger:hover { color: var(--danger-hover); }
|
||||||
/* Selected segmented-toggle button (view-settings modal). */
|
/* Selected segmented-toggle button (view-settings modal). */
|
||||||
@@ -403,6 +404,8 @@ main > h2 {
|
|||||||
.dropdown-menu.is-open { display: block; }
|
.dropdown-menu.is-open { display: block; }
|
||||||
.dropdown-menu.scrollable { max-height: 23rem; overflow-y: auto; }
|
.dropdown-menu.scrollable { max-height: 23rem; overflow-y: auto; }
|
||||||
|
|
||||||
|
.actions-special > .btn {text-align: left;}
|
||||||
|
|
||||||
/* === Suggestion dropdown (header search + editor link picker) ===
|
/* === Suggestion dropdown (header search + editor link picker) ===
|
||||||
Anchored to a position:relative host. Mirrors .dropdown-menu visuals with
|
Anchored to a position:relative host. Mirrors .dropdown-menu visuals with
|
||||||
a dashed border and bg-panel-hover for the active row. */
|
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);
|
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 === */
|
||||||
.companion-status { margin-left: auto; }
|
.companion-status { margin-left: auto; }
|
||||||
.companion-icon { font-size: 0.9rem; line-height: 1; padding: 0.1rem 0.3rem; }
|
.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. */
|
actions right. */
|
||||||
header { grid-template-columns: auto 1fr auto; }
|
header { grid-template-columns: auto 1fr auto; }
|
||||||
.search-form { width: 100%; max-width: none; justify-self: stretch; }
|
.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. */
|
/* Show the actions menu items directly in the bar in desktop size */
|
||||||
button.fab { display: inline-flex; }
|
@media (min-width: 1101px) {
|
||||||
.fab.dropdown { bottom: calc(var(--space-4) + 3rem + var(--space-2)); }
|
.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) {
|
@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 () {
|
(function () {
|
||||||
// This script owns the .tree-scroll child of the aside, not the aside itself.
|
// This script owns the .tree-scroll child of the aside, not the aside itself.
|
||||||
var container = document.querySelector('aside.tree-sidebar .tree-scroll');
|
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
|
function setupMenuItem() {
|
||||||
// opens the tree's scroll container in the full-viewport Overlay
|
var specials = document.querySelector('.actions-special');
|
||||||
// (overlay.js). The container always exists when not editing, so — per the
|
if (!specials) return;
|
||||||
// layout spec — the FAB always renders; the overlay auto-closes when a
|
|
||||||
// folder/file link inside it navigates. Hidden on desktop by .fab CSS.
|
var item = document.createElement("button");
|
||||||
function setupFab() {
|
item.type = "button";
|
||||||
var fab = document.createElement('button');
|
item.className = "btn btn-block";
|
||||||
fab.type = 'button';
|
item.title = "Folders";
|
||||||
fab.className = 'btn btn-fab fab fab-tree';
|
item.textContent = "FOLDERS";
|
||||||
fab.title = 'Folder tree';
|
item.addEventListener("click", function () {
|
||||||
fab.setAttribute('aria-label', 'Folder tree');
|
if (typeof openOverlay === "function") openOverlay(aside);
|
||||||
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);
|
specials.appendChild(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
render();
|
render();
|
||||||
setupFab();
|
setupMenuItem();
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user