frontend: Legende scrollbar + Splitter zwischen Editor und Legende
Die Legende wurde abgeschnitten statt zu scrollen — obwohl `.hint` seit jeher `flex:1 1 auto; min-height:0; overflow:auto` trägt. Ursache: Chrome legt den Inhalt eines <details> in das Pseudo-Element `::details-content`. Damit war `.hint` kein Flex-Kind von `.agenda` mehr, die Begrenzung griff nicht und der Kasten wuchs auf seine Inhaltshöhe (gemessen 585 px in einem 282 px hohen Container) und wurde geclippt. Eine Regel für `::details-content` wirkte im Test, ist aber Chrome-eigen — Firefox und Safari kennen das Pseudo-Element nicht. - Die Legende ist deshalb kein <details> mehr, sondern `div.agenda` + `button.agenda-summary`; der Zustand hängt an der Klasse `open` (`aria-expanded` am Button, `#legendBtn` und Splitter-Sichtbarkeit über `setAgendaOpen()` synchron). `.hint` ist wieder echtes Flex-Kind und scrollt in allen Browsern. - Neuer Splitter `#hintGutter` im selben Idiom wie der große (Pointer-Capture, Doppelklick setzt zurück): horizontal nebeneinander -> Breite `--hcol`, gestapelt (side/mobil) -> Höhe `--hrow`. Beide getrennt gehalten und in `werkbaum-ui` persistiert (samt Auf-/Zu-Zustand), damit ein Moduswechsel die jeweils andere Aufteilung nicht zerstört. Grenzen 90 px bis 85 %. - Die 85-%-Obergrenze steht zusätzlich als max-width/max-height im CSS: die gespeicherte Größe ist ein fester px-Wert und würde den Editor sonst auf 0 drücken, wenn das Panel später schrumpft (großer Splitter, Drehung, Bildschirmtastatur). Verifiziert mit --hrow:9999px -> gekappt bei 85 %. - Nebenbei: der große Splitter war als einziges Bedienelement nicht lokalisiert (fest deutsche title/aria-label). Beide teilen sich nun `gutterTooltip`, dazu `gutterAria`/`hintGutterAria` — 9 Sprachen. Verifiziert im Browser: Legende scrollt (247 px Fenster, 607 px Inhalt); Ziehen in beiden Ausrichtungen inkl. Unter-/Obergrenze und Doppelklick-Reset; `--hcol` und `--hrow` überleben den Moduswechsel; nach Neuladen sind Auf-Zustand, Breite und beide Variablen wiederhergestellt; auf Mobil ist die Summary-Zeile aus und der Kopf-Knopf steuert. Vitest 37/37. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4061362922
commit
6888762ac8
@@ -586,3 +586,36 @@ Verworfen: die Tastatur zuzulassen und auf die OS-Einstellung zu verweisen (lös
|
|||||||
es nur auf einem Gerät), sowie ein eigener Umschalter dafür (weiteres
|
es nur auf einem Gerät), sowie ein eigener Umschalter dafür (weiteres
|
||||||
Bedienelement in einer engen Kopfzeile plus i18n in 9 Sprachen, für ein
|
Bedienelement in einer engen Kopfzeile plus i18n in 9 Sprachen, für ein
|
||||||
Verhalten, das kaum jemand umstellen will).
|
Verhalten, das kaum jemand umstellen will).
|
||||||
|
|
||||||
|
## D26 — Legende scrollbar: eigener Container statt `<details>`, plus Splitter
|
||||||
|
Die Legende („Agenda") im Editor-Panel war zu hoch für ihren Platz und wurde
|
||||||
|
**abgeschnitten** statt scrollbar zu sein — obwohl `.hint` seit jeher
|
||||||
|
`flex:1 1 auto; min-height:0; overflow:auto` trug.
|
||||||
|
|
||||||
|
**Ursache:** Chrome legt den Inhalt eines `<details>` seit einiger Zeit in das
|
||||||
|
Pseudo-Element **`::details-content`**. Damit ist `.hint` **kein Flex-Kind** von
|
||||||
|
`.agenda` mehr; die Flex-Begrenzung greift nicht, `.hint` wächst auf seine
|
||||||
|
Inhaltshöhe (gemessen 585 px in einem 282 px hohen Container) und wird vom
|
||||||
|
`overflow:hidden` der Agenda geclippt. Ein Gegenmittel wäre
|
||||||
|
`.agenda::details-content{display:flex;…}` (im Test wirksam: 585 → 248 px,
|
||||||
|
scrollbar), aber das Pseudo-Element ist Chrome-eigen — Firefox und Safari kennen
|
||||||
|
es nicht, dort hinge dieselbe Layout-Kette an einem anderen anonymen Kasten.
|
||||||
|
|
||||||
|
**Entscheidung: kein `<details>` mehr.** Die Legende ist ein gewöhnliches
|
||||||
|
`<div class="agenda">` mit `<button class="agenda-summary">`; der Auf-/Zu-Zustand
|
||||||
|
hängt an der Klasse `open` (`aria-expanded` am Button). Damit ist `.hint` wieder
|
||||||
|
ein echtes Flex-Kind und die Begrenzung in **allen** Browsern dieselbe. Die
|
||||||
|
native Aufklapp-Mechanik war ohnehin nur halb genutzt — mobil steuert seit D17
|
||||||
|
der `#legendBtn` im Kopf, das `toggle`-Ereignis diente nur der Synchronisierung.
|
||||||
|
|
||||||
|
**Splitter Editor|Legende.** Die Aufteilung ist nun frei ziehbar, im selben
|
||||||
|
Idiom wie der große Splitter (`pointerdown`/`pointermove`/Pointer-Capture,
|
||||||
|
Doppelklick setzt zurück). Die Ausrichtung folgt derselben Fallunterscheidung
|
||||||
|
wie dort: horizontal nebeneinander → Legendenbreite `--hcol`, gestapelt (`side`
|
||||||
|
oder mobil) → Legendenhöhe `--hrow`. **Beide Werte werden getrennt gehalten und
|
||||||
|
persistiert** (`werkbaum-ui`), sodass ein Moduswechsel die jeweils andere
|
||||||
|
Aufteilung nicht zerstört; der Auf-/Zu-Zustand der Legende wird mitgesichert.
|
||||||
|
Grenzen: mindestens 90 px, höchstens 85 % — die Obergrenze steht **zusätzlich
|
||||||
|
als `max-width`/`max-height` im CSS**, weil die gespeicherte Größe ein fester
|
||||||
|
px-Wert ist: schrumpft das Panel später (Zug am großen Splitter, Drehung,
|
||||||
|
Bildschirmtastatur), würde der Editor sonst auf 0 gedrückt.
|
||||||
|
|||||||
@@ -225,6 +225,16 @@ Scheitert das Laden (häufigster Fall: das Ziel sendet keinen
|
|||||||
`Access-Control-Allow-Origin`-Header, außerdem 404/Netzfehler), bleibt der
|
`Access-Control-Allow-Origin`-Header, außerdem 404/Netzfehler), bleibt der
|
||||||
bisherige Stand stehen und es erscheint eine **Warnung**. Siehe D23.
|
bisherige Stand stehen und es erscheint eine **Warnung**. Siehe D23.
|
||||||
|
|
||||||
|
### Legende im Editor-Panel
|
||||||
|
Neben dem Textfeld steht eine aufklappbare **Legende** (Notation in Kurzform,
|
||||||
|
abschließend eine Bedienungs-Zeile). Sie ist **scrollbar**, wenn ihr Inhalt
|
||||||
|
höher ist als der Platz, und über einen eigenen **Splitter** vom Textfeld
|
||||||
|
abgeteilt: horizontal nebeneinander (Legendenbreite), in den gestapelten Modi
|
||||||
|
und auf kleinem Bildschirm untereinander (Legendenhöhe). Ziehen verteilt frei,
|
||||||
|
Doppelklick stellt die Vorgabe wieder her; die Aufteilung bleibt für beide
|
||||||
|
Ausrichtungen getrennt erhalten. Die Legende belegt höchstens 85 % des Panels,
|
||||||
|
damit das Textfeld nie ganz verschwindet. Siehe D26.
|
||||||
|
|
||||||
### Sprung zwischen Diagramm und Text
|
### Sprung zwischen Diagramm und Text
|
||||||
Jeder Knoten kennt seine Zeilennummer im Notationstext; beide Richtungen sind
|
Jeder Knoten kennt seine Zeilennummer im Notationstext; beide Richtungen sind
|
||||||
verknüpft (siehe D25):
|
verknüpft (siehe D25):
|
||||||
|
|||||||
@@ -153,6 +153,17 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der
|
|||||||
Handler am Textfeld hebt das wieder auf (läuft vor dem Fokus). Wer sonst
|
Handler am Textfeld hebt das wieder auf (läuft vor dem Fokus). Wer sonst
|
||||||
irgendwo `src.focus()` ergänzt und dabei Tippen meint, muss vorher
|
irgendwo `src.focus()` ergänzt und dabei Tippen meint, muss vorher
|
||||||
`keyboardOnJump(false)` rufen — so wie `newDoc()`.
|
`keyboardOnJump(false)` rufen — so wie `newDoc()`.
|
||||||
|
- Legende (D26): **kein `<details>`** — Chrome legt Details-Inhalt in
|
||||||
|
`::details-content`, dadurch war `.hint` kein Flex-Kind mehr und ließ sich nicht
|
||||||
|
begrenzen (Inhalt geclippt statt scrollbar). Jetzt `div.agenda` +
|
||||||
|
`button.agenda-summary`, Zustand an der Klasse `open` (`setAgendaOpen()` hält
|
||||||
|
Klasse, `aria-expanded`, `#legendBtn` und die Sichtbarkeit des Splitters
|
||||||
|
zusammen). Wer hier wieder ein natives Aufklapp-Element einsetzt, bricht das
|
||||||
|
Scrollen. Der Splitter `#hintGutter` schreibt `--hcol` (nebeneinander) bzw.
|
||||||
|
`--hrow` (gestapelt: `side` oder mobil) an `#app`; beide werden getrennt in
|
||||||
|
`werkbaum-ui` gesichert. Die 85-%-Obergrenze steht **zusätzlich** als
|
||||||
|
`max-width`/`max-height` im CSS — die gespeicherte px-Größe würde den Editor
|
||||||
|
sonst erdrücken, wenn das Panel später schrumpft.
|
||||||
- Kleiner Bildschirm: `body.mobile` (per `matchMedia`, ≤ 640 px) stapelt
|
- Kleiner Bildschirm: `body.mobile` (per `matchMedia`, ≤ 640 px) stapelt
|
||||||
Diagramm/Editor mit **stufenlosem** Splitter (kein Snap/Collapse wie auf
|
Diagramm/Editor mit **stufenlosem** Splitter (kein Snap/Collapse wie auf
|
||||||
Desktop): der Gutter-Drag ruft `setMobileDrow()` (klemmt `--drow` zwischen den
|
Desktop): der Gutter-Drag ruft `setMobileDrow()` (klemmt `--drow` zwischen den
|
||||||
|
|||||||
+5
-4
@@ -73,8 +73,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="editor-body">
|
<div class="editor-body">
|
||||||
<textarea id="src" spellcheck="false" data-i18n-aria="editorTitle" aria-label="Struktur (Text)"></textarea>
|
<textarea id="src" spellcheck="false" data-i18n-aria="editorTitle" aria-label="Struktur (Text)"></textarea>
|
||||||
<details class="agenda">
|
<div class="hint-gutter" id="hintGutter" role="separator" data-i18n-title="gutterTooltip" data-i18n-aria="hintGutterAria" title="Ziehen zum Verschieben, Doppelklick setzt zurück" aria-label="Editor und Legende größenverändern" hidden></div>
|
||||||
<summary data-i18n="agenda">Agenda</summary>
|
<div class="agenda" id="agenda">
|
||||||
|
<button type="button" class="agenda-summary" id="agendaToggle" aria-expanded="false" aria-controls="hint" data-i18n="agenda">Agenda</button>
|
||||||
<div class="hint" id="hint">
|
<div class="hint" id="hint">
|
||||||
Einrückung (2 Leerzeichen oder Tab) definiert die Hierarchie.<br>
|
Einrückung (2 Leerzeichen oder Tab) definiert die Hierarchie.<br>
|
||||||
<code>-</code> Teilpaket, alle erforderlich <em>(all of)</em><br>
|
<code>-</code> Teilpaket, alle erforderlich <em>(all of)</em><br>
|
||||||
@@ -96,11 +97,11 @@
|
|||||||
Kommentare mit <code>%%</code> — als ganze Zeile oder am Zeilenende.
|
Kommentare mit <code>%%</code> — als ganze Zeile oder am Zeilenende.
|
||||||
Personen mit <code>@name</code> — erscheinen unten rechts am Knoten.
|
Personen mit <code>@name</code> — erscheinen unten rechts am Knoten.
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gutter" id="gutter" role="separator" aria-label="Bereiche größenverändern" title="Ziehen zum Verschieben"></div>
|
<div class="gutter" id="gutter" role="separator" data-i18n-title="gutterTooltip" data-i18n-aria="gutterAria" aria-label="Bereiche größenverändern" title="Ziehen zum Verschieben, Doppelklick setzt zurück"></div>
|
||||||
|
|
||||||
<div class="panel right">
|
<div class="panel right">
|
||||||
<div class="panel-head">
|
<div class="panel-head">
|
||||||
|
|||||||
+88
-5
@@ -770,6 +770,8 @@ const I18N = {
|
|||||||
downloadMenu:"Diagramm herunterladen (SVG/PNG)",
|
downloadMenu:"Diagramm herunterladen (SVG/PNG)",
|
||||||
minimize:"minimieren", normal:"normal", maximize:"maximieren",
|
minimize:"minimieren", normal:"normal", maximize:"maximieren",
|
||||||
agenda:"Agenda", discarded:"verworfene",
|
agenda:"Agenda", discarded:"verworfene",
|
||||||
|
gutterTooltip:"Ziehen zum Verschieben, Doppelklick setzt zurück", gutterAria:"Bereiche größenverändern",
|
||||||
|
hintGutterAria:"Editor und Legende größenverändern",
|
||||||
modeHorizontal:"Horizontal – Organigramm, Diagramm über dem Editor",
|
modeHorizontal:"Horizontal – Organigramm, Diagramm über dem Editor",
|
||||||
modeKompakt:"Kompakt – alles nach unten, platzsparend",
|
modeKompakt:"Kompakt – alles nach unten, platzsparend",
|
||||||
modeVertikal:"Vertikal – Baum nach rechts, Diagramm neben dem Editor",
|
modeVertikal:"Vertikal – Baum nach rechts, Diagramm neben dem Editor",
|
||||||
@@ -818,6 +820,8 @@ const I18N = {
|
|||||||
downloadMenu:"Download diagram (SVG/PNG)",
|
downloadMenu:"Download diagram (SVG/PNG)",
|
||||||
minimize:"minimize", normal:"normal", maximize:"maximize",
|
minimize:"minimize", normal:"normal", maximize:"maximize",
|
||||||
agenda:"Legend", discarded:"discarded",
|
agenda:"Legend", discarded:"discarded",
|
||||||
|
gutterTooltip:"Drag to resize, double-click resets", gutterAria:"Resize the areas",
|
||||||
|
hintGutterAria:"Resize editor and legend",
|
||||||
modeHorizontal:"Horizontal – org chart, diagram above the editor",
|
modeHorizontal:"Horizontal – org chart, diagram above the editor",
|
||||||
modeKompakt:"Compact – everything downward, space-saving",
|
modeKompakt:"Compact – everything downward, space-saving",
|
||||||
modeVertikal:"Vertical – tree to the right, diagram beside the editor",
|
modeVertikal:"Vertical – tree to the right, diagram beside the editor",
|
||||||
@@ -866,6 +870,8 @@ const I18N = {
|
|||||||
downloadMenu:"Descargar el diagrama (SVG/PNG)",
|
downloadMenu:"Descargar el diagrama (SVG/PNG)",
|
||||||
minimize:"minimizar", normal:"normal", maximize:"maximizar",
|
minimize:"minimizar", normal:"normal", maximize:"maximizar",
|
||||||
agenda:"Leyenda", discarded:"descartados",
|
agenda:"Leyenda", discarded:"descartados",
|
||||||
|
gutterTooltip:"Arrastra para redimensionar, doble clic restablece", gutterAria:"Redimensionar las áreas",
|
||||||
|
hintGutterAria:"Redimensionar editor y leyenda",
|
||||||
modeHorizontal:"Horizontal – organigrama, diagrama sobre el editor",
|
modeHorizontal:"Horizontal – organigrama, diagrama sobre el editor",
|
||||||
modeKompakt:"Compacto – todo hacia abajo, ahorra espacio",
|
modeKompakt:"Compacto – todo hacia abajo, ahorra espacio",
|
||||||
modeVertikal:"Vertical – árbol hacia la derecha, diagrama junto al editor",
|
modeVertikal:"Vertical – árbol hacia la derecha, diagrama junto al editor",
|
||||||
@@ -914,6 +920,8 @@ const I18N = {
|
|||||||
downloadMenu:"Télécharger le diagramme (SVG/PNG)",
|
downloadMenu:"Télécharger le diagramme (SVG/PNG)",
|
||||||
minimize:"réduire", normal:"normal", maximize:"agrandir",
|
minimize:"réduire", normal:"normal", maximize:"agrandir",
|
||||||
agenda:"Légende", discarded:"abandonnés",
|
agenda:"Légende", discarded:"abandonnés",
|
||||||
|
gutterTooltip:"Glisser pour redimensionner, double-clic pour réinitialiser", gutterAria:"Redimensionner les zones",
|
||||||
|
hintGutterAria:"Redimensionner l'éditeur et la légende",
|
||||||
modeHorizontal:"Horizontal – organigramme, diagramme au-dessus de l'éditeur",
|
modeHorizontal:"Horizontal – organigramme, diagramme au-dessus de l'éditeur",
|
||||||
modeKompakt:"Compact – tout vers le bas, gain de place",
|
modeKompakt:"Compact – tout vers le bas, gain de place",
|
||||||
modeVertikal:"Vertical – arbre vers la droite, diagramme à côté de l'éditeur",
|
modeVertikal:"Vertical – arbre vers la droite, diagramme à côté de l'éditeur",
|
||||||
@@ -962,6 +970,8 @@ const I18N = {
|
|||||||
downloadMenu:"Pobierz diagram (SVG/PNG)",
|
downloadMenu:"Pobierz diagram (SVG/PNG)",
|
||||||
minimize:"minimalizuj", normal:"normalny", maximize:"maksymalizuj",
|
minimize:"minimalizuj", normal:"normalny", maximize:"maksymalizuj",
|
||||||
agenda:"Legenda", discarded:"odrzucone",
|
agenda:"Legenda", discarded:"odrzucone",
|
||||||
|
gutterTooltip:"Przeciągnij, aby zmienić rozmiar; dwuklik przywraca", gutterAria:"Zmień rozmiar obszarów",
|
||||||
|
hintGutterAria:"Zmień rozmiar edytora i legendy",
|
||||||
modeHorizontal:"Poziomo – schemat organizacyjny, diagram nad edytorem",
|
modeHorizontal:"Poziomo – schemat organizacyjny, diagram nad edytorem",
|
||||||
modeKompakt:"Kompaktowo – wszystko w dół, oszczędza miejsce",
|
modeKompakt:"Kompaktowo – wszystko w dół, oszczędza miejsce",
|
||||||
modeVertikal:"Pionowo – drzewo w prawo, diagram obok edytora",
|
modeVertikal:"Pionowo – drzewo w prawo, diagram obok edytora",
|
||||||
@@ -1010,6 +1020,8 @@ const I18N = {
|
|||||||
downloadMenu:"Скачать диаграмму (SVG/PNG)",
|
downloadMenu:"Скачать диаграмму (SVG/PNG)",
|
||||||
minimize:"свернуть", normal:"обычный", maximize:"развернуть",
|
minimize:"свернуть", normal:"обычный", maximize:"развернуть",
|
||||||
agenda:"Легенда", discarded:"отклонённые",
|
agenda:"Легенда", discarded:"отклонённые",
|
||||||
|
gutterTooltip:"Потяните, чтобы изменить размер; двойной щелчок сбрасывает", gutterAria:"Изменить размер областей",
|
||||||
|
hintGutterAria:"Изменить размер редактора и легенды",
|
||||||
modeHorizontal:"Горизонтально – оргсхема, диаграмма над редактором",
|
modeHorizontal:"Горизонтально – оргсхема, диаграмма над редактором",
|
||||||
modeKompakt:"Компактно – всё вниз, экономит место",
|
modeKompakt:"Компактно – всё вниз, экономит место",
|
||||||
modeVertikal:"Вертикально – дерево вправо, диаграмма рядом с редактором",
|
modeVertikal:"Вертикально – дерево вправо, диаграмма рядом с редактором",
|
||||||
@@ -1058,6 +1070,8 @@ const I18N = {
|
|||||||
downloadMenu:"आरेख डाउनलोड करें (SVG/PNG)",
|
downloadMenu:"आरेख डाउनलोड करें (SVG/PNG)",
|
||||||
minimize:"छोटा करें", normal:"सामान्य", maximize:"बड़ा करें",
|
minimize:"छोटा करें", normal:"सामान्य", maximize:"बड़ा करें",
|
||||||
agenda:"लेजेंड", discarded:"अस्वीकृत",
|
agenda:"लेजेंड", discarded:"अस्वीकृत",
|
||||||
|
gutterTooltip:"आकार बदलने के लिए खींचें, डबल-क्लिक रीसेट करता है", gutterAria:"क्षेत्रों का आकार बदलें",
|
||||||
|
hintGutterAria:"संपादक और लेजेंड का आकार बदलें",
|
||||||
modeHorizontal:"क्षैतिज – संगठन-चार्ट, संपादक के ऊपर आरेख",
|
modeHorizontal:"क्षैतिज – संगठन-चार्ट, संपादक के ऊपर आरेख",
|
||||||
modeKompakt:"सघन – सब नीचे की ओर, जगह बचाता है",
|
modeKompakt:"सघन – सब नीचे की ओर, जगह बचाता है",
|
||||||
modeVertikal:"लंबवत – पेड़ दाईं ओर, संपादक के बगल में आरेख",
|
modeVertikal:"लंबवत – पेड़ दाईं ओर, संपादक के बगल में आरेख",
|
||||||
@@ -1106,6 +1120,8 @@ const I18N = {
|
|||||||
downloadMenu:"下载图表(SVG/PNG)",
|
downloadMenu:"下载图表(SVG/PNG)",
|
||||||
minimize:"最小化", normal:"正常", maximize:"最大化",
|
minimize:"最小化", normal:"正常", maximize:"最大化",
|
||||||
agenda:"图例", discarded:"已放弃",
|
agenda:"图例", discarded:"已放弃",
|
||||||
|
gutterTooltip:"拖动可调整大小,双击可重置", gutterAria:"调整区域大小",
|
||||||
|
hintGutterAria:"调整编辑器和图例大小",
|
||||||
modeHorizontal:"横向——组织结构图,图表在编辑器上方",
|
modeHorizontal:"横向——组织结构图,图表在编辑器上方",
|
||||||
modeKompakt:"紧凑——全部向下,节省空间",
|
modeKompakt:"紧凑——全部向下,节省空间",
|
||||||
modeVertikal:"纵向——树向右展开,图表在编辑器旁边",
|
modeVertikal:"纵向——树向右展开,图表在编辑器旁边",
|
||||||
@@ -1154,6 +1170,8 @@ const I18N = {
|
|||||||
downloadMenu:"ダイアグラムをダウンロード(SVG/PNG)",
|
downloadMenu:"ダイアグラムをダウンロード(SVG/PNG)",
|
||||||
minimize:"最小化", normal:"標準", maximize:"最大化",
|
minimize:"最小化", normal:"標準", maximize:"最大化",
|
||||||
agenda:"凡例", discarded:"破棄",
|
agenda:"凡例", discarded:"破棄",
|
||||||
|
gutterTooltip:"ドラッグでサイズ変更、ダブルクリックでリセット", gutterAria:"領域のサイズを変更",
|
||||||
|
hintGutterAria:"エディターと凡例のサイズを変更",
|
||||||
modeHorizontal:"横 — 組織図、ダイアグラムはエディターの上",
|
modeHorizontal:"横 — 組織図、ダイアグラムはエディターの上",
|
||||||
modeKompakt:"コンパクト — すべて下方向、省スペース",
|
modeKompakt:"コンパクト — すべて下方向、省スペース",
|
||||||
modeVertikal:"縦 — ツリーを右へ、ダイアグラムはエディターの横",
|
modeVertikal:"縦 — ツリーを右へ、ダイアグラムはエディターの横",
|
||||||
@@ -1353,6 +1371,12 @@ function saveUI(){
|
|||||||
split: splitState,
|
split: splitState,
|
||||||
col: app.style.getPropertyValue('--col') || null,
|
col: app.style.getPropertyValue('--col') || null,
|
||||||
drow: app.style.getPropertyValue('--drow') || null,
|
drow: app.style.getPropertyValue('--drow') || null,
|
||||||
|
/* Legende: auf/zu + Aufteilung Editor|Legende (D26). Bewusst per
|
||||||
|
DOM-Abfrage statt über die `agenda`-Konstante — die wird erst weiter
|
||||||
|
unten deklariert, und saveUI() läuft schon aus applySplit() heraus. */
|
||||||
|
agenda: !!document.querySelector('.agenda.open'),
|
||||||
|
hcol: app.style.getPropertyValue('--hcol') || null,
|
||||||
|
hrow: app.style.getPropertyValue('--hrow') || null,
|
||||||
zoom: zoom,
|
zoom: zoom,
|
||||||
fullscreen: document.body.classList.contains('fullscreen')
|
fullscreen: document.body.classList.contains('fullscreen')
|
||||||
}));
|
}));
|
||||||
@@ -1387,6 +1411,10 @@ function restoreState(){
|
|||||||
if(ui && ui.col) app.style.setProperty('--col', ui.col);
|
if(ui && ui.col) app.style.setProperty('--col', ui.col);
|
||||||
if(ui && ui.drow) app.style.setProperty('--drow', ui.drow);
|
if(ui && ui.drow) app.style.setProperty('--drow', ui.drow);
|
||||||
}
|
}
|
||||||
|
/* Legende (D26): Aufteilung gilt unabhängig vom Preset des großen Splitters. */
|
||||||
|
if(ui && ui.hcol) app.style.setProperty('--hcol', ui.hcol);
|
||||||
|
if(ui && ui.hrow) app.style.setProperty('--hrow', ui.hrow);
|
||||||
|
setAgendaOpen(!!(ui && ui.agenda));
|
||||||
applyZoom();
|
applyZoom();
|
||||||
restoring = false;
|
restoring = false;
|
||||||
}
|
}
|
||||||
@@ -1595,13 +1623,68 @@ window.addEventListener('orientationchange', setAppHeight);
|
|||||||
if(window.visualViewport){
|
if(window.visualViewport){
|
||||||
window.visualViewport.addEventListener('resize', setAppHeight);
|
window.visualViewport.addEventListener('resize', setAppHeight);
|
||||||
}
|
}
|
||||||
|
/* ---------- Legende: auf/zu + Splitter zum Editor (D26) ----------
|
||||||
|
Die Legende ist ein gewöhnlicher Container (kein <details> mehr, siehe
|
||||||
|
style.css/D26); der Auf-/Zu-Zustand hängt an der Klasse `open`. */
|
||||||
const legendBtn = document.getElementById('legendBtn');
|
const legendBtn = document.getElementById('legendBtn');
|
||||||
const agenda = document.querySelector('.agenda');
|
const agenda = document.getElementById('agenda');
|
||||||
legendBtn.addEventListener('click', () => {
|
const agendaToggle = document.getElementById('agendaToggle');
|
||||||
agenda.open = !agenda.open;
|
const hintGutter = document.getElementById('hintGutter');
|
||||||
legendBtn.classList.toggle('active', agenda.open);
|
const editorBody = document.querySelector('.editor-body');
|
||||||
|
function agendaOpen(){ return agenda.classList.contains('open'); }
|
||||||
|
function setAgendaOpen(on){
|
||||||
|
agenda.classList.toggle('open', on);
|
||||||
|
agendaToggle.setAttribute('aria-expanded', on ? 'true' : 'false');
|
||||||
|
legendBtn.classList.toggle('active', on);
|
||||||
|
hintGutter.hidden = !on; /* ohne offene Legende gibt es nichts zu teilen */
|
||||||
|
saveUI();
|
||||||
|
}
|
||||||
|
legendBtn.addEventListener('click', () => setAgendaOpen(!agendaOpen()));
|
||||||
|
agendaToggle.addEventListener('click', () => setAgendaOpen(!agendaOpen()));
|
||||||
|
|
||||||
|
/* Splitter Editor|Legende. Im horizontalen Modus liegen sie nebeneinander
|
||||||
|
(Legendenbreite `--hcol`), in den gestapelten Modi untereinander
|
||||||
|
(Legendenhöhe `--hrow`) — dieselbe Fallunterscheidung wie beim großen
|
||||||
|
Splitter. Gezogen wird von der Editorseite aus, die Legende behält also
|
||||||
|
den Abstand zum jeweils gegenüberliegenden Rand. */
|
||||||
|
const HINT_MIN = 90; /* Titelzeile + ein paar Zeilen bleiben immer da */
|
||||||
|
const HINT_MAX_SHARE = 0.85;
|
||||||
|
function editorStacked(){ return isMobile() || app.classList.contains('side'); }
|
||||||
|
let hintDragging = false;
|
||||||
|
hintGutter.addEventListener('pointerdown', e => {
|
||||||
|
hintDragging = true;
|
||||||
|
hintGutter.classList.add('dragging');
|
||||||
|
hintGutter.setPointerCapture(e.pointerId);
|
||||||
|
document.body.style.userSelect = 'none';
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
hintGutter.addEventListener('pointermove', e => {
|
||||||
|
if(!hintDragging) return;
|
||||||
|
const b = editorBody.getBoundingClientRect();
|
||||||
|
if(editorStacked()){
|
||||||
|
const h = Math.min(Math.max(b.bottom - e.clientY, HINT_MIN), b.height * HINT_MAX_SHARE);
|
||||||
|
app.style.setProperty('--hrow', Math.round(h) + 'px');
|
||||||
|
} else {
|
||||||
|
const w = Math.min(Math.max(b.right - e.clientX, HINT_MIN), b.width * HINT_MAX_SHARE);
|
||||||
|
app.style.setProperty('--hcol', Math.round(w) + 'px');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function endHintDrag(e){
|
||||||
|
if(!hintDragging) return;
|
||||||
|
hintDragging = false;
|
||||||
|
hintGutter.classList.remove('dragging');
|
||||||
|
document.body.style.userSelect = '';
|
||||||
|
try{ hintGutter.releasePointerCapture(e.pointerId); }catch(_){}
|
||||||
|
saveUI();
|
||||||
|
}
|
||||||
|
hintGutter.addEventListener('pointerup', endHintDrag);
|
||||||
|
hintGutter.addEventListener('pointercancel', endHintDrag);
|
||||||
|
/* Doppelklick stellt die Vorgabegröße wieder her (CSS-Default greift dann). */
|
||||||
|
hintGutter.addEventListener('dblclick', () => {
|
||||||
|
app.style.removeProperty('--hcol');
|
||||||
|
app.style.removeProperty('--hrow');
|
||||||
|
saveUI();
|
||||||
});
|
});
|
||||||
agenda.addEventListener('toggle', () => legendBtn.classList.toggle('active', agenda.open));
|
|
||||||
/* Modus-Wähler auf kleinem Bildschirm: es ist nur das aktive Icon sichtbar,
|
/* Modus-Wähler auf kleinem Bildschirm: es ist nur das aktive Icon sichtbar,
|
||||||
Tippen schaltet zum nächsten Modus (reihum). Auf normaler Größe bleibt es
|
Tippen schaltet zum nächsten Modus (reihum). Auf normaler Größe bleibt es
|
||||||
der Dreier-Umschalter — dort kehrt der Handler sofort zurück. */
|
der Dreier-Umschalter — dort kehrt der Handler sofort zurück. */
|
||||||
|
|||||||
+34
-12
@@ -208,28 +208,48 @@
|
|||||||
Horizontal nebeneinander, vertikal (.side) untereinander. */
|
Horizontal nebeneinander, vertikal (.side) untereinander. */
|
||||||
.editor-body{display:flex;align-items:stretch;flex:1 1 auto;min-height:0}
|
.editor-body{display:flex;align-items:stretch;flex:1 1 auto;min-height:0}
|
||||||
.editor-body textarea{flex:1 1 auto;min-width:0;min-height:0}
|
.editor-body textarea{flex:1 1 auto;min-width:0;min-height:0}
|
||||||
|
/* Legende: bewusst KEIN <details> mehr. Chrome legt den Details-Inhalt in
|
||||||
|
`::details-content`; dadurch war `.hint` kein Flex-Kind von `.agenda` mehr,
|
||||||
|
`flex:1/min-height:0` liefen ins Leere und der Inhalt wurde geclippt statt
|
||||||
|
scrollbar (D26). Als gewöhnlicher Container ist die Kette wieder dicht. */
|
||||||
.agenda{
|
.agenda{
|
||||||
flex:0 0 auto;
|
flex:0 0 auto;
|
||||||
border-left:1px dashed rgba(36,52,71,.18);
|
border-left:1px dashed rgba(36,52,71,.18);
|
||||||
display:flex;flex-direction:column;
|
display:flex;flex-direction:column;min-height:0;min-width:0;
|
||||||
}
|
}
|
||||||
.agenda[open]{width:300px}
|
/* Die Aufteilung steht als feste px-Größe. Schrumpft das Panel später (Zug am
|
||||||
.agenda>summary{
|
großen Splitter, Drehung, Tastatur), begrenzt max-* die Legende, damit der
|
||||||
list-style:none;cursor:pointer;white-space:nowrap;
|
Editor nicht auf 0 gedrückt wird — gleiche Schranke wie beim Ziehen. */
|
||||||
|
.agenda.open{width:var(--hcol,300px);max-width:85%;overflow:hidden}
|
||||||
|
.agenda-summary{
|
||||||
|
flex:0 0 auto;
|
||||||
|
background:none;border:0;text-align:left;
|
||||||
|
cursor:pointer;white-space:nowrap;
|
||||||
padding:10px 16px;
|
padding:10px 16px;
|
||||||
font-family:'IBM Plex Mono',monospace;
|
font-family:'IBM Plex Mono',monospace;
|
||||||
font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
|
font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
|
||||||
display:flex;align-items:center;gap:7px;
|
display:flex;align-items:center;gap:7px;
|
||||||
}
|
}
|
||||||
.agenda>summary::-webkit-details-marker{display:none}
|
.agenda-summary::before{content:'▸';display:inline-block;transition:transform .15s}
|
||||||
.agenda>summary::before{content:'▸';display:inline-block;transition:transform .15s}
|
.agenda.open>.agenda-summary::before{transform:rotate(90deg)}
|
||||||
.agenda[open]>summary::before{transform:rotate(90deg)}
|
.agenda-summary:focus-visible{outline:2px solid var(--or);outline-offset:-2px}
|
||||||
.agenda{min-height:0}
|
.agenda:not(.open) .hint{display:none}
|
||||||
.agenda[open]{overflow:hidden}
|
|
||||||
.agenda .hint{border-top:0;flex:1 1 auto;min-height:0;overflow:auto}
|
.agenda .hint{border-top:0;flex:1 1 auto;min-height:0;overflow:auto}
|
||||||
|
/* Splitter zwischen Editor und Legende — dieselbe Sprache wie der große
|
||||||
|
Splitter, nur schmaler. Ohne offene Legende gibt es nichts zu teilen. */
|
||||||
|
.hint-gutter{
|
||||||
|
flex:0 0 10px;align-self:stretch;
|
||||||
|
cursor:col-resize;touch-action:none;
|
||||||
|
display:flex;align-items:center;justify-content:center;
|
||||||
|
}
|
||||||
|
.hint-gutter[hidden]{display:none}
|
||||||
|
.hint-gutter::before{content:'';width:3px;height:38px;border-radius:2px;background:rgba(36,52,71,.18)}
|
||||||
|
.hint-gutter:hover::before,.hint-gutter.dragging::before{background:var(--or)}
|
||||||
.side .editor-body{flex-direction:column;align-items:stretch}
|
.side .editor-body{flex-direction:column;align-items:stretch}
|
||||||
.side .agenda{border-left:0;border-top:1px dashed rgba(36,52,71,.18)}
|
.side .agenda{border-left:0;border-top:1px dashed rgba(36,52,71,.18)}
|
||||||
.side .agenda[open]{width:auto}
|
.side .agenda.open{width:auto;max-width:none;height:var(--hrow,220px);max-height:85%}
|
||||||
|
.side .hint-gutter{cursor:row-resize}
|
||||||
|
.side .hint-gutter::before{width:38px;height:3px}
|
||||||
|
|
||||||
.panel{
|
.panel{
|
||||||
background:var(--card);
|
background:var(--card);
|
||||||
@@ -713,9 +733,11 @@
|
|||||||
/* Editor immer gestapelt (Textarea oben, Legende darunter) */
|
/* Editor immer gestapelt (Textarea oben, Legende darunter) */
|
||||||
body.mobile .editor-body{flex-direction:column;align-items:stretch}
|
body.mobile .editor-body{flex-direction:column;align-items:stretch}
|
||||||
body.mobile .agenda{border-left:0;border-top:1px dashed rgba(36,52,71,.18)}
|
body.mobile .agenda{border-left:0;border-top:1px dashed rgba(36,52,71,.18)}
|
||||||
body.mobile .agenda[open]{width:auto}
|
body.mobile .agenda.open{width:auto;max-width:none;height:var(--hrow,220px);max-height:85%}
|
||||||
|
body.mobile .hint-gutter{cursor:row-resize}
|
||||||
|
body.mobile .hint-gutter::before{width:38px;height:3px}
|
||||||
/* Legende: keine dauerhafte Summary-Zeile, dafür der #legendBtn im Kopf */
|
/* Legende: keine dauerhafte Summary-Zeile, dafür der #legendBtn im Kopf */
|
||||||
body.mobile .agenda>summary{display:none}
|
body.mobile .agenda-summary{display:none}
|
||||||
body.mobile .legendbtn{display:inline-flex}
|
body.mobile .legendbtn{display:inline-flex}
|
||||||
/* Sprachwahl schlank: eingeklappt nur die aktive Sprache. Ein Tipp klappt die
|
/* Sprachwahl schlank: eingeklappt nur die aktive Sprache. Ein Tipp klappt die
|
||||||
volle Leiste als Overlay über die Kopfzeile auf (verdeckt fsToggle usw.,
|
volle Leiste als Overlay über die Kopfzeile auf (verdeckt fsToggle usw.,
|
||||||
|
|||||||
Reference in New Issue
Block a user