Anpassung Menü
This commit is contained in:
128
web/html/js/appbar.js
Normal file
128
web/html/js/appbar.js
Normal file
@@ -0,0 +1,128 @@
|
||||
// Gemeinsame Kopfleiste für alle Ansichten.
|
||||
//
|
||||
// Vorher hatte jede Ansicht ihre eigene Zeile aus Textlinks. Bei sechs
|
||||
// Einträgen unterschiedlicher Länge - "Drucken", "Märkte & Gruppen",
|
||||
// "Teilen (2)" - brach das auf schmalen Geräten unregelmäßig um, und die
|
||||
// Grundlinien lagen versetzt.
|
||||
//
|
||||
// Jetzt: links der Weg zurück, rechts ein Menü, dessen Einträge
|
||||
// untereinander stehen. Eine einzelne Aktion wird direkt angezeigt -
|
||||
// ein Menü mit einem Eintrag wäre ein Klick zu viel.
|
||||
"use strict";
|
||||
|
||||
import { el } from "./dom.js";
|
||||
|
||||
let openPanel = null;
|
||||
|
||||
/** Schließt ein offenes Menü. Wird von aussen aufgerufen, wenn die
|
||||
* Ansicht wechselt - sonst bliebe das Menü über den Wechsel hinweg
|
||||
* stehen. */
|
||||
export function closeMenu() {
|
||||
if (!openPanel) return;
|
||||
openPanel.panel.hidden = true;
|
||||
openPanel.button.setAttribute("aria-expanded", "false");
|
||||
document.removeEventListener("click", openPanel.onOutside, true);
|
||||
document.removeEventListener("keydown", openPanel.onKey);
|
||||
openPanel = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {{label: string, onClick?: Function}} [options.back]
|
||||
* @param {string} [options.title] Kontext, etwa der Listenname
|
||||
* @param {Array<{label: string, onClick?: Function, href?: string,
|
||||
* danger?: boolean, hint?: string}>} [options.actions]
|
||||
*/
|
||||
export function appBar({ back, title, actions = [] } = {}) {
|
||||
closeMenu();
|
||||
const usable = actions.filter(Boolean);
|
||||
|
||||
const left = back
|
||||
? el("button.bar-back", { type: "button", onclick: back.onClick },
|
||||
"\u2039 " + back.label)
|
||||
: el("span.bar-back", {});
|
||||
|
||||
const middle = title ? el("span.bar-title", { title }, title) : el("span", {});
|
||||
|
||||
function entry(action) {
|
||||
const content = [
|
||||
el("span.entry-label", {}, action.label),
|
||||
action.hint ? el("span.entry-hint", {}, action.hint) : null,
|
||||
];
|
||||
|
||||
// Ein Verweis bleibt ein Verweis: So funktionieren "In neuem Tab
|
||||
// öffnen" und das Kopieren der Adresse wie erwartet.
|
||||
const node = action.href
|
||||
? el("a", {
|
||||
href: action.href,
|
||||
target: action.target || "_blank",
|
||||
rel: "noopener",
|
||||
className: action.danger ? "danger" : "",
|
||||
onclick: () => closeMenu(),
|
||||
}, content)
|
||||
: el("button", {
|
||||
type: "button",
|
||||
className: action.danger ? "danger" : "",
|
||||
onclick: () => { closeMenu(); action.onClick?.(); },
|
||||
}, content);
|
||||
|
||||
return el("li", {}, node);
|
||||
}
|
||||
|
||||
let right;
|
||||
if (usable.length === 0) {
|
||||
right = el("span", {});
|
||||
} else if (usable.length === 1) {
|
||||
const only = usable[0];
|
||||
right = only.href
|
||||
? el("a.bar-single", {
|
||||
href: only.href, target: only.target || "_blank", rel: "noopener",
|
||||
}, only.label)
|
||||
: el("button.bar-single", { type: "button", onclick: only.onClick },
|
||||
only.label);
|
||||
} else {
|
||||
const panel = el("ul.menu-panel", { hidden: true }, usable.map(entry));
|
||||
const button = el("button.burger", {
|
||||
type: "button",
|
||||
"aria-haspopup": "true",
|
||||
"aria-expanded": "false",
|
||||
"aria-label": "Menü",
|
||||
title: "Menü",
|
||||
},
|
||||
el("span.burger-lines", { "aria-hidden": "true" }),
|
||||
el("span.burger-text", {}, "Menü"));
|
||||
|
||||
button.addEventListener("click", (ev) => {
|
||||
ev.stopPropagation();
|
||||
if (openPanel && openPanel.panel === panel) {
|
||||
closeMenu();
|
||||
return;
|
||||
}
|
||||
closeMenu();
|
||||
|
||||
panel.hidden = false;
|
||||
button.setAttribute("aria-expanded", "true");
|
||||
|
||||
const onOutside = (event) => {
|
||||
if (!panel.contains(event.target) && event.target !== button) {
|
||||
closeMenu();
|
||||
}
|
||||
};
|
||||
const onKey = (event) => {
|
||||
if (event.key === "Escape") {
|
||||
closeMenu();
|
||||
button.focus();
|
||||
}
|
||||
};
|
||||
// In der Erfassungsphase, damit auch Klicks auf Elemente greifen,
|
||||
// die ihrerseits die Weiterleitung stoppen.
|
||||
document.addEventListener("click", onOutside, true);
|
||||
document.addEventListener("keydown", onKey);
|
||||
openPanel = { panel, button, onOutside, onKey };
|
||||
});
|
||||
|
||||
right = el("div.bar-menu", {}, button, panel);
|
||||
}
|
||||
|
||||
return el("header.app-bar", {}, left, middle, right);
|
||||
}
|
||||
Reference in New Issue
Block a user