129 lines
4.1 KiB
JavaScript
129 lines
4.1 KiB
JavaScript
// 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);
|
|
}
|