// 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); }