diff --git a/web/app/src/menue.ts b/web/app/src/menue.ts index 7497507..1256822 100644 --- a/web/app/src/menue.ts +++ b/web/app/src/menue.ts @@ -22,7 +22,7 @@ export type Menuepunkt = { export function kontextmenue(ziel: HTMLElement, punkte: () => Menuepunkt[]): void { ziel.addEventListener('contextmenu', e => { e.preventDefault(); - oeffnen(e.clientX, e.clientY, punkte()); + oeffnen(e.clientX, e.clientY, punkte(), ziel); }); // Die Kontextmenü-Taste und Shift+F10 — dieselbe Geste ohne Maus. @@ -30,7 +30,7 @@ export function kontextmenue(ziel: HTMLElement, punkte: () => Menuepunkt[]): voi if (e.key !== 'ContextMenu' && !(e.key === 'F10' && e.shiftKey)) return; e.preventDefault(); const kasten = ziel.getBoundingClientRect(); - oeffnen(kasten.left + kasten.width / 2, kasten.top + kasten.height / 2, punkte()); + oeffnen(kasten.left + kasten.width / 2, kasten.top + kasten.height / 2, punkte(), ziel); }); // Langer Druck. 500 ms, und jede Bewegung bricht ab — sonst löst Scrollen @@ -42,7 +42,7 @@ export function kontextmenue(ziel: HTMLElement, punkte: () => Menuepunkt[]): voi const f = e.touches[0]; if (!f) return; start = { x: f.clientX, y: f.clientY }; - druck = window.setTimeout(() => oeffnen(start.x, start.y, punkte()), 500); + druck = window.setTimeout(() => oeffnen(start.x, start.y, punkte(), ziel), 500); }, { passive: true }); const abbrechen = () => { if (druck !== undefined) { clearTimeout(druck); druck = undefined; } }; @@ -63,20 +63,36 @@ export function kontextmenue(ziel: HTMLElement, punkte: () => Menuepunkt[]): voi */ export function menueAnZiel(ziel: HTMLElement, punkte: Menuepunkt[]): void { const kasten = ziel.getBoundingClientRect(); - oeffnen(kasten.right, kasten.bottom + 4, punkte); + oeffnen(kasten.right, kasten.bottom + 4, punkte, ziel); } /** Das offene Menü — immer höchstens eines. */ let offen: HTMLElement | null = null; +/** Woran es hängt — der Knopf, der es geöffnet hat. + * + * **Weil der öffnende Klick noch unterwegs ist.** Er blubbert nach dem + * eigenen Zuhörer weiter zum Dokument, und der dortige schliesst alles, was + * nicht im Menü liegt — also auch dieses, im selben Augenblick. In der + * Mediathek stand deshalb ein `stopPropagation()` am Knopf; in der Prüfliste + * fehlte es, und das Menü ging gar nicht auf. Eine Regel, an die sich jeder + * Aufrufer erinnern muss, ist keine Regel. Jetzt merkt sich das Menü selbst, + * woran es hängt. + */ +let ausloeser: HTMLElement | null = null; + export function menueSchliessen(): void { offen?.remove(); offen = null; + ausloeser = null; } -function oeffnen(x: number, y: number, punkte: Menuepunkt[]): void { +function oeffnen( + x: number, y: number, punkte: Menuepunkt[], woran: HTMLElement | null = null, +): void { menueSchliessen(); if (punkte.length === 0) return; + ausloeser = woran; const menue = document.createElement('div'); menue.className = 'kontextmenue'; @@ -118,7 +134,11 @@ function oeffnen(x: number, y: number, punkte: Menuepunkt[]): void { // registriert und nicht je Kachel — sonst hingen an vierhundert Kacheln // zwölfhundert Zuhörer am Dokument. document.addEventListener('click', e => { - if (offen && !offen.contains(e.target as Node)) menueSchliessen(); + if (!offen) return; + const ziel = e.target as Node; + // Der Klick auf den Griff hat es gerade geöffnet — er schliesst es nicht. + if (offen.contains(ziel) || ausloeser?.contains(ziel)) return; + menueSchliessen(); }); document.addEventListener('keydown', e => { if (e.key === 'Escape') menueSchliessen(); }); window.addEventListener('scroll', menueSchliessen, { passive: true });