From fe818b6c2c9662337952a90ea02f386cb92c4c41 Mon Sep 17 00:00:00 2001 From: mhoennig Date: Tue, 21 Jul 2026 19:09:01 +0200 Subject: [PATCH] frontend: Mobile Sprachwahl als Overlay, eingeklappt nur aktive Sprache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - eingeklappt zeigt die Leiste nur noch die aktive Sprache (kein EN-Default, kein „…“); ein Tipp klappt die volle Liste als Overlay über die Kopfzeile auf (position:absolute, z-index) statt die Zeile zu verbreitern - nach Auswahl klappt sie wieder auf die gewählte Sprache ein; Tipp daneben schließt ebenfalls; applyMobile() startet eingeklappt - nur body.mobile betroffen; Desktop (DE EN ES FR + „…“, inline) unverändert - SPEC §9 (Kleiner Bildschirm) und DECISIONS D17 nachgeführt Co-Authored-By: Claude Opus 4.8 --- docs/DECISIONS.md | 8 ++++++-- docs/SPEC.md | 11 +++++++---- frontend/index.html | 39 +++++++++++++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 9b92132..ce6810d 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -145,8 +145,12 @@ Fenster-Buttons entfallen, der **Splitter bleibt** (Teilen per Ziehen möglich). Die Legende wird von der Bereichs- Umschaltung **entkoppelt** und über einen eigenen Umschalter im Editor-Kopf gesteuert — das dauerhafte „AGENDA“-Band kostet sonst zu viel Höhe. Die -Sprachleiste zeigt nur Englisch + aktive Sprache + „…“. Voreinstellung ist -**Vollbild** (maximale Fläche). Umgesetzt über die Klasse `body.mobile` (per +Sprachleiste zeigt eingeklappt nur die **aktive** Sprache; ein Tipp klappt die +volle Leiste als **Overlay über die Kopfzeile** auf (rechtsbündig, `z-index`, +verdeckt die übrigen Elemente statt die Zeile umzubrechen), nach der Auswahl — +oder einem Tipp daneben — klappt sie wieder ein. So bleibt die Kopfzeile schmal +und die vollständige Sprachliste dennoch erreichbar (frühere Lösung +„EN + aktive + …“ ersetzt). Voreinstellung ist **Vollbild** (maximale Fläche). Umgesetzt über die Klasse `body.mobile` (per `matchMedia`), damit CSS und JS denselben Schwellwert teilen; die Vollbild- Voreinstellung greift nur, wenn noch **keine** gespeicherte Nutzerwahl vorliegt (eine bewusste Abschaltung bleibt so erhalten, siehe localStorage-Persistenz). diff --git a/docs/SPEC.md b/docs/SPEC.md index 8309dd6..599ccd1 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -153,10 +153,13 @@ Min/Normal/Max-Buttons entfallen; geteilt wird weiterhin über den **Splitter** Umschalter im Editor-Kopf (statt der dauerhaften „AGENDA“-Zeile), damit sie zugeklappt keinen Platz kostet. Der **Modus-Wähler** zeigt nur das aktive Icon und schaltet bei jedem Tippen reihum weiter (horizontal → kompakt → vertikal → -…), spart also zwei Drittel der Breite. Die **Sprachwahl** zeigt nur Englisch -und die aktive Sprache plus „…“ (das alle Sprachen aufklappt). Der **Untertitel** -wird auf eine garantiert einzeilige Kurzfassung reduziert. Voreinstellung auf -kleinem Bildschirm ist **Vollbild** (siehe D17). +…), spart also zwei Drittel der Breite. Die **Sprachwahl** zeigt eingeklappt +nur die aktive Sprache; ein Tipp darauf klappt die volle Leiste als **Overlay +über die Kopfzeile** auf (verdeckt die übrigen Bedienelemente, statt die Zeile +zu verbreitern), nach der Auswahl klappt sie wieder auf die gewählte Sprache +ein (Tipp daneben schließt ebenfalls). Der **Untertitel** wird auf eine +garantiert einzeilige Kurzfassung reduziert. Voreinstellung auf kleinem +Bildschirm ist **Vollbild** (siehe D17). ### Günstigster Pfad (Kosten-Hervorhebung) Ein Umschalter (Icon-Button im Diagramm-Kopf, Voreinstellung **an**, Zustand diff --git a/frontend/index.html b/frontend/index.html index 718b44b..ed08733 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -550,11 +550,20 @@ /* Legende: keine dauerhafte Summary-Zeile, dafür der #legendBtn im Kopf */ body.mobile .agenda>summary{display:none} body.mobile .legendbtn{display:inline-flex} - /* Sprachwahl schlank: nur EN + aktuelle Sprache + „…“; „…“ klappt alle auf */ + /* Sprachwahl schlank: eingeklappt nur die aktive Sprache. Ein Tipp klappt die + volle Leiste als Overlay über die Kopfzeile auf (verdeckt fsToggle usw., + statt die Zeile zu verbreitern); nach der Auswahl klappt sie wieder ein + (JS). Das „…“ entfällt hier — die aktive Sprache ist selbst der Auslöser. */ + body.mobile .header-tools{position:relative} body.mobile .langsel button[data-lang]{display:none} - body.mobile .langsel.expanded button[data-lang]{display:inline-block} - body.mobile .langsel button[data-lang="en"], body.mobile .langsel button[data-lang].active{display:inline-block} + body.mobile .langmore{display:none} + body.mobile .langsel.expanded{ + position:absolute;top:0;right:0;z-index:40; + background:var(--card); + box-shadow:0 6px 18px rgba(36,52,71,.28); + } + body.mobile .langsel.expanded button[data-lang]{display:inline-block} /* Modus-Wähler kompakt: nur das aktive Icon (ein Drittel Platz), Tippen schaltet reihum weiter (horizontal → kompakt → vertikal → …, siehe JS). */ body.mobile .seg label{display:none} @@ -1697,8 +1706,29 @@ langMore.addEventListener('click', () => { const exp = langsel.classList.toggle('expanded'); langMore.setAttribute('aria-expanded', String(exp)); }); +function collapseLangsel(){ + langsel.classList.remove('expanded'); + langMore.setAttribute('aria-expanded', 'false'); +} document.querySelectorAll('.langsel button[data-lang]').forEach(b => { - b.addEventListener('click', () => applyLang(b.dataset.lang)); + b.addEventListener('click', () => { + /* Mobil: eingeklappt ist nur die aktive Sprache sichtbar — ein Tipp klappt + die volle Leiste als Overlay auf; erst im aufgeklappten Zustand wählt ein + Tipp die Sprache und klappt wieder ein. */ + if(isMobile() && !langsel.classList.contains('expanded')){ + langsel.classList.add('expanded'); + langMore.setAttribute('aria-expanded', 'true'); + return; + } + applyLang(b.dataset.lang); + if(isMobile()) collapseLangsel(); + }); +}); +/* Mobil: Tipp außerhalb der aufgeklappten Leiste schließt sie wieder. */ +document.addEventListener('click', e => { + if(isMobile() && langsel.classList.contains('expanded') && !langsel.contains(e.target)){ + collapseLangsel(); + } }); /* ---------- Vollbild: Panels über die ganze Fensterbreite ---------- */ @@ -1797,6 +1827,7 @@ seg.addEventListener('click', e => { function applyMobile(){ const m = isMobile(); document.body.classList.toggle('mobile', m); + if(m) collapseLangsel(); /* Sprachleiste eingeklappt starten (Overlay-Logik) */ if(!m) return; /* Nur ein Panel sichtbar: normal/custom passen nicht — auf Diagramm (b). */ if(splitState !== 'a' && splitState !== 'b'){ splitState = 'b'; applySplit(); }