feat(diagram): Knoten-Fenster öffnet nur noch auf Klick, nicht mehr bei Hover (D92, SPEC §6/§9)
Der einfache Klick toggelt das Fenster überall — wie der Tipp auf Touch (D52); der Link eines Knotens ist damit auch am Zeiger der ↗-Knopf im Fenster, Enter und Strg+Klick öffnen die URL weiterhin direkt. Tastatur- fokus öffnet nur noch echten (:focus-visible), focusout schließt nicht in das Fenster hinein, und die Fokus-Rückgabe nach dem Falten ruft das Fenster ausdrücklich nie. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a223e6af57
commit
0fe4c07851
@@ -19,6 +19,8 @@ reverse.
|
||||
|
||||
## 2026-08-28
|
||||
|
||||
- The node window no longer opens on hover: click a node to open it, click again or elsewhere to close — folding and moving across the tree stay snappy
|
||||
- A linked node's plain click now opens the window everywhere, like on touch — the URL sits behind the ↗ button, and Enter on the focused node or Ctrl+click still open it directly
|
||||
- The Taiga buttons in the node window sit side by side in one row now — the window grows a little wider for them, and they wrap only when space truly runs out
|
||||
- The open button moved into the ticket box's action row, next to write and take, and its label shrank to "Open #US-123" — the neighbours say where
|
||||
- "Link ticket" in the node window binds an existing Taiga story or task to a node: paste the ticket's URL — it carries project, type and number by itself — or enter a ref or bare number
|
||||
|
||||
@@ -8568,3 +8568,68 @@ längsten Labels): drei Anlege-Knöpfe eine Zeile, „→ nach Taiga schreiben
|
||||
Ref-Knoten ohne Projekt-Slug (keine Knöpfe) fällt auf 336px zurück —
|
||||
`tipwide` ist weg. 628 Tests unverändert (reine UI-Verdrahtung,
|
||||
Browser-geprüft — die D54-Nachtrag-3-Grenze).
|
||||
|
||||
## D92 — Das Knoten-Fenster öffnet nur noch auf Klick, nicht mehr beim Überfahren
|
||||
Gemeldet vom Nutzer: „Mit mehr Funktionalität in dem Popup, so dass es auch
|
||||
träger geworden ist als der einfache Tooltip vorher, gibt es ein
|
||||
Usability-Problem: Wenn man nur einen Knoten auf-/zuklappen will, kommt schon
|
||||
das Popup." Das trifft die Sache: D57 hat dem Fenster den Hover gegeben, als
|
||||
es noch ein besserer Tooltip war — Beschreibung plus Kurz-Fakten. Seit D91
|
||||
trägt es Ticket-Stand, Anlege-, Verknüpfen- und Abgleich-Knöpfe und ist
|
||||
breiter geworden (`tipwide`, D91-Nachtrag 12); beim Ansteuern des Falt-Chips
|
||||
stand es regelmäßig schon im Weg. Ein Fenster mit Aktionen ist kein Tooltip
|
||||
mehr — es verdient eine ausdrückliche Geste.
|
||||
|
||||
**Der einfache Klick öffnet, ein zweiter (oder daneben) schließt** — wörtlich
|
||||
die Touch-Regel aus D52, jetzt überall. Damit fällt zwangsläufig eine
|
||||
Entscheidung über die **verlinkten Knoten** (§6: der einfache Klick öffnete
|
||||
dort die URL): Eine Geste kann nicht beides tun, und D25 hat ausdrücklich
|
||||
abgelehnt, dass derselbe Klick je nach Knoten Verschiedenes tut. Also gilt
|
||||
die D52-Abwägung jetzt auch am Zeiger — „ansehen ist die häufigere Absicht,
|
||||
und der Link bleibt einen Klick entfernt": Der **↗-Knopf steht in jedem
|
||||
Fenster eines verlinkten Knotens**, nicht mehr nur auf Touch. Direkt zur URL
|
||||
führen weiterhin **Enter** am fokussierten Knoten (der native Weg; im
|
||||
Klick-Handler an `e.detail === 0` erkannt und durchgelassen) und
|
||||
**Strg+Klick** (Browser-Default, Hintergrund-Tab — auf Zeilen mit
|
||||
Ticket-Referenz gewinnt das Taiga-Öffnen, D91-Nachtrag 5, wie bisher).
|
||||
Alt+Klick (Sprung, D25) und der Falt-Chip (D38) sind im Handler ausgenommen
|
||||
und bleiben, was sie sind.
|
||||
|
||||
**Tastaturfokus öffnet weiter sofort — aber nur echten.** Das war die
|
||||
verdeckte Falle des Umbaus: Auch der Mausklick fokussiert den Knoten, und der
|
||||
`focusin`-Handler (D57) hätte das Fenster geöffnet, bevor der Klick-Toggle es
|
||||
für „schon offen" hielte und sofort wieder schlösse — netto bliebe es zu.
|
||||
Unterschieden wird mit `:focus-visible`, der Browser-eigenen Heuristik, die
|
||||
D25-Nachtrag 2 für genau diese Frage schon vermessen hat (nach Mausklick
|
||||
falsch, nach Tab wahr). Nebenwirkung, bewusst hingenommen: Auch der
|
||||
programmatische Fokus nach Alt+Klick aus dem Text und nach dem
|
||||
Stationen-Knopf (D47) folgt der Heuristik — nach einer Maus-Geste öffnet dort
|
||||
kein Fenster mehr mit. Das passt zur Stoßrichtung dieses Eintrags: weniger
|
||||
ungefragte Fenster.
|
||||
|
||||
**Und `focusout` darf den Knopf-Klick nicht mehr töten.** Mit dem Klick ist
|
||||
der Knoten beim offenen Fenster jetzt regelmäßig **fokussiert** — vorher
|
||||
(Hover) war er das nicht, und der `focusout`-Schließer war harmlos. Die
|
||||
Knöpfe im Fenster sind trotz `tabindex="-1"` klick-fokussierbar: mousedown
|
||||
auf einen Taiga-Knopf blurte den Knoten, `focusout` schlösse das Fenster
|
||||
zwischen mousedown und mouseup, und der Klick ginge an einem
|
||||
`display:none`-Element verloren. Deshalb schließt `focusout` nicht, wenn
|
||||
`relatedTarget` im Fenster liegt; Tab zum nächsten Knoten schließt weiter.
|
||||
|
||||
**Ersatzlos weg:** die 350-ms-Öffnungs- und 120-ms-Schließ-Timer, die
|
||||
Sofort-Weiterreiche bei schon offenem Fenster, `pointerenter`/`pointerleave`
|
||||
am Fenster und die `finePointer`-Weiche — das Fenster ist jetzt auf allen
|
||||
Geräten dieselbe Klick-Sache, und ein einmal geöffnetes bleibt stehen, bis
|
||||
etwas es ausdrücklich schließt (zweiter Klick, Klick daneben, Esc, ×,
|
||||
Scrollen, Neubau — die D52-Liste, unverändert). Die 400-ms-Verzögerung des
|
||||
Ticket-Abrufs (D91-Nachtrag 6) bleibt: Sie schützt jetzt das Durch-Tabben
|
||||
statt des Vorüberfahrens, und der Bulk-Cache (D91-Nachtrag 10) macht sie
|
||||
ohnehin meist unsichtbar.
|
||||
|
||||
**Der Preis, benannt:** Beim schnellen Überfliegen eines fremden Plans
|
||||
zeigte der Hover die Beschreibungen ohne Klick — das ist weg, je Knoten
|
||||
kostet es jetzt einen Klick. Die ”-Marke (D40) und der ↗ hinter dem Label
|
||||
sagen weiterhin, wo sich einer lohnt. Und ein Doppelklick öffnet und
|
||||
schließt in einem Zug — hinnehmbar, auf Knoten doppelklickt niemand
|
||||
absichtlich.
|
||||
|
||||
|
||||
+29
-23
@@ -414,13 +414,16 @@ Größe als **Bereich** gelesen (Untergrenzen verdoppeln sich):
|
||||
|
||||
## 6. Links
|
||||
|
||||
- Ein nacktes `https://…`-Token macht den ganzen Knoten klickbar
|
||||
(neuer Tab, `rel="noopener"`); Kennzeichnung mit ↗ hinter dem Label.
|
||||
- **Ausnahme auf Touch-Geräten:** Dort öffnet der einfache Tipp nicht die URL,
|
||||
sondern das Knoten-Fenster (§9) — der Link steht darin als eigener Knopf. Ohne
|
||||
Zeiger gibt es keinen Tooltip, und ein einziger Tipp kann nicht beides tun;
|
||||
„ansehen" ist die häufigere Absicht, und der Link bleibt einen Tipp entfernt.
|
||||
Siehe D52.
|
||||
- Ein nacktes `https://…`-Token verlinkt den Knoten; Kennzeichnung mit ↗
|
||||
hinter dem Label.
|
||||
- Geöffnet wird die URL über den **↗-Knopf im Knoten-Fenster** (§9; neuer
|
||||
Tab, `rel="noopener"`): Der einfache Klick wie der einfache Tipp öffnen das
|
||||
**Fenster**, nicht die URL — eine Geste kann nicht beides tun, „ansehen"
|
||||
ist die häufigere Absicht, und der Link bleibt einen Klick entfernt
|
||||
(D52 für Touch, D92 für den Zeiger). **Enter** am fokussierten Knoten und
|
||||
**Strg+Klick** (Browser-Default, Hintergrund-Tab) öffnen die URL weiterhin
|
||||
direkt — auf Zeilen mit Ticket-Referenz gewinnt bei Strg+Klick das
|
||||
Taiga-Öffnen (§9).
|
||||
|
||||
## 7. Personen-Tags
|
||||
|
||||
@@ -469,11 +472,13 @@ Grafikexport: Der Text selbst kann dort nicht angezeigt werden, eine Marke
|
||||
ohne Ziel wäre Rauschen. Siehe D40.
|
||||
|
||||
**Gezeigt wird das immer im eigenen Knoten-Fenster**, nicht im Tooltip des
|
||||
Browsers: am Zeiger nach kurzer Verzögerung beim Überfahren, bei Tastaturfokus
|
||||
sofort, auf Touch beim einfachen Tipp (§6). Ein `title` kann weder Absätze noch
|
||||
eine Linie und erscheint nie beim Tastaturfokus; das Fenster kann beides. Der
|
||||
Inhalt ist derselbe — die Beschreibung als Absatz, die Kurz-Fakten dahinter
|
||||
durch eine echte Linie abgesetzt. Siehe D57.
|
||||
Browsers: beim **einfachen Klick bzw. Tipp** auf den Knoten (ein zweiter
|
||||
schließt), bei **Tastaturfokus** sofort. Beim bloßen Überfahren mit dem
|
||||
Zeiger öffnet es **nicht** — das tat es zwischenzeitlich (D57) und stand
|
||||
damit dem Falten und dem Bewegen über den Baum im Weg (D92). Ein `title`
|
||||
kann weder Absätze noch eine Linie und erscheint nie beim Tastaturfokus; das
|
||||
Fenster kann beides. Der Inhalt ist derselbe — die Beschreibung als Absatz,
|
||||
die Kurz-Fakten dahinter durch eine echte Linie abgesetzt. Siehe D57, D92.
|
||||
|
||||
- Das Fenster hängt am angetippten Knoten (kleine Spitze zu ihm hin) und weicht
|
||||
nach oben aus, wenn unten kein Platz ist. Der Knoten trägt, solange es offen
|
||||
@@ -481,13 +486,12 @@ durch eine echte Linie abgesetzt. Siehe D57.
|
||||
- Es schließt beim Tipp daneben, beim erneuten Tipp auf denselben Knoten, mit
|
||||
**Esc**, über sein ×, beim Scrollen des Diagramms und bei jedem Neubau.
|
||||
- Ein **verlinkter** Knoten (§6) bekommt darin einen ↗-Knopf, der die URL im
|
||||
neuen Tab öffnet — auf Touch ist das der Weg zum Link.
|
||||
- Der **Sprung-Hinweis** nennt hier den langen Druck statt Alt+Klick (§9,
|
||||
neuen Tab öffnet — der einfache Klick/Tipp gehört dem Fenster (§6, D92).
|
||||
- Der **Sprung-Hinweis** nennt auf Touch den langen Druck statt Alt+Klick (§9,
|
||||
Sprung zwischen Diagramm und Text) — Alt gibt es auf dem Telefon nicht.
|
||||
- Reine Bedienhilfe: nicht im Grafikexport, nicht im Druck. Auf Geräten mit
|
||||
Zeiger ändert sich nichts, dort bleibt es beim Tooltip.
|
||||
- Reine Bedienhilfe: nicht im Grafikexport, nicht im Druck.
|
||||
|
||||
Siehe D52.
|
||||
Siehe D52, D92.
|
||||
|
||||
**Ticket-Stand im Knoten-Fenster (§11).** Trägt die Zeile eine
|
||||
Ticket-Referenz (`#US-123` / `#T-1234`) und ist ihr Teilbaum per
|
||||
@@ -519,8 +523,9 @@ es einen gibt, den **Zuständigen**.
|
||||
Farbe des Knotens ist die Rückmeldung.
|
||||
- Beide Richtungen betreffen **einen** Knoten; eine Sammelaktion über einen
|
||||
Teilbaum gibt es nicht.
|
||||
- Geholt wird erst, wenn das Fenster **kurz stehen bleibt** (nicht im
|
||||
Vorüberfahren), und je Ticket **einmal je Sitzung** — ein ↻-Knopf im Fenster
|
||||
- Geholt wird erst, wenn das Fenster **kurz stehen bleibt** (nicht beim
|
||||
Durch-Tabben von Knoten zu Knoten), und je Ticket **einmal je Sitzung** —
|
||||
ein ↻-Knopf im Fenster
|
||||
holt neu. Ohne Anmeldung an der Instanz, ohne Projekt-Zuordnung oder ohne
|
||||
konfiguriertes Backend geschieht still nichts; ein Fehler steht als Zeile im
|
||||
Fenster.
|
||||
@@ -1086,8 +1091,9 @@ verknüpft (siehe D25):
|
||||
freistehend — dort behält Strg+Klick den Sprung zur Zeile; die
|
||||
D67-Ausschlüsse (Kommentar, Beschreibungsteil, URL) gelten auch hier.
|
||||
Siehe D91-Nachtrag 5.
|
||||
- Der **einfache** Klick bleibt unverändert der Link (§6): Ein Knoten mit URL
|
||||
öffnet sie weiterhin im neuen Tab. Deshalb Alt und nicht der einfache Klick.
|
||||
- Der **einfache** Klick gehört dem Knoten-Fenster (§6/§9, D92): Er öffnet die
|
||||
Beschreibung, nicht die Zeile im Text. Deshalb Alt und nicht der einfache
|
||||
Klick.
|
||||
- **Auffindbarkeit:** Solange **Alt gedrückt** ist, zeigen alle Knoten den
|
||||
Sprung-Cursor und der Knoten unter dem Zeiger einen Petrol-Ring — die Geste
|
||||
ist im Moment des Ausprobierens sichtbar, auch auf verlinkten Knoten. Dazu
|
||||
@@ -1207,8 +1213,8 @@ Label: **▾** offen, **„▸ n“** eingeklappt (n = Zahl der verborgenen Knot
|
||||
als kleiner **gerahmter Chip**, damit das Klickziel zu treffen ist (D64; das
|
||||
nackte Glyph war zu klein). Klick auf das Zeichen klappt um — der einfache
|
||||
Klick auf den Knoten selbst
|
||||
bleibt der Link (§6); Tastatur: **←** klappt zu, **→** klappt auf am
|
||||
fokussierten Knoten (WAI-ARIA-Baum-Idiom).
|
||||
öffnet das Knoten-Fenster (§9, D92); Tastatur: **←** klappt zu, **→** klappt
|
||||
auf am fokussierten Knoten (WAI-ARIA-Baum-Idiom).
|
||||
|
||||
- **Anfangszustand aus dem Text (§1):** `>` klappt ein. `<` holt seinen
|
||||
Teilbaum hervor, indem die Faltung die Pfad-Ebenen **hinunterwandert**:
|
||||
|
||||
@@ -654,6 +654,15 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der
|
||||
Es schließt bei allem, was seine Aussage hinfällig macht — Tipp daneben,
|
||||
zweiter Tipp, Esc, ×, Diagramm-Scroll, Bereichswechsel, Sprung in den Text,
|
||||
`render()`. Die letzten drei sind beim Bauen zuerst vergessen worden.
|
||||
- **Das Knoten-Fenster öffnet auf Klick, nie auf Hover (D92):** Der einfache
|
||||
Klick toggelt es überall (auch auf Link-Knoten — die URL ist der ↗-Knopf im
|
||||
Fenster; Enter kommt als Klick mit `e.detail === 0` und wird durchgelassen).
|
||||
Zwei Fallen: `focusin` öffnet nur bei `:focus-visible` — der Mausklick
|
||||
fokussiert den Knoten ebenfalls, und ohne die Weiche schlösse der
|
||||
Klick-Toggle das vom focusin geöffnete Fenster sofort wieder. Und
|
||||
`focusout` schließt nicht, wenn `relatedTarget` im Fenster liegt — die
|
||||
Knöpfe darin sind trotz `tabindex="-1"` klick-fokussierbar, sonst stirbt
|
||||
der Knopf-Klick zwischen mousedown und mouseup.
|
||||
- **Taiga-Ticket-Anlage (D91):** Die entscheidbaren Regeln liegen headless in
|
||||
`taiga.js` (`ticketRefOf` = Idempotenz-Marker, `taskCandidates` =
|
||||
Dialog-Vorbelegung, `storyAncestor` = der nächste Vorfahr mit Story-Ref,
|
||||
|
||||
+49
-48
@@ -1462,7 +1462,18 @@ function applyFoldPreset(mode){
|
||||
/* Faltung umklappen (SPEC §9, D38). Gelingt das Zurückschreiben, ist der Text
|
||||
der Zustand — die Überlagerungen werden dann geleert, damit sie ihn nicht
|
||||
maskieren können. Nach dem Neubau bekommt derselbe Knoten den Fokus zurück,
|
||||
sonst risse die Tastaturbedienung ab (das alte Element ist weg). */
|
||||
sonst risse die Tastaturbedienung ab (das alte Element ist weg) — aber ohne
|
||||
das Knoten-Fenster zu rufen: Falten ist genau die Geste, der das Fenster im
|
||||
Weg stand (D92), und die `:focus-visible`-Heuristik ist beim
|
||||
programmatischen Fokus nicht verlässlich genug für diese Zusage. */
|
||||
let suppressTipFocus = false;
|
||||
function refocusNode(line){
|
||||
const again = out.querySelector('.node[data-line="' + line + '"]');
|
||||
if(!again) return;
|
||||
suppressTipFocus = true; /* focusin läuft synchron im focus() */
|
||||
again.focus({preventScroll: true});
|
||||
suppressTipFocus = false;
|
||||
}
|
||||
function toggleFold(el){
|
||||
const line = +el.dataset.line;
|
||||
const st = foldByLine.get(line);
|
||||
@@ -1473,16 +1484,14 @@ function toggleFold(el){
|
||||
if(lens){
|
||||
lensOverrides.set(st.key, !st.collapsed);
|
||||
render();
|
||||
const again2 = out.querySelector('.node[data-line="' + line + '"]');
|
||||
if(again2) again2.focus({preventScroll: true});
|
||||
refocusNode(line);
|
||||
return;
|
||||
}
|
||||
foldOverrides.set(st.key, !st.collapsed);
|
||||
/* Das Schreiben löst per `input`-Ereignis schon ein render() aus. */
|
||||
if(writeFoldToText(line, !st.collapsed)) foldOverrides.clear();
|
||||
else render();
|
||||
const again = out.querySelector('.node[data-line="' + line + '"]');
|
||||
if(again) again.focus({preventScroll: true});
|
||||
refocusNode(line);
|
||||
}
|
||||
|
||||
/* Klick auf das Falt-Zeichen ▾/▸ klappt um. preventDefault, weil das Zeichen
|
||||
@@ -1577,7 +1586,7 @@ out.addEventListener('touchend', e => {
|
||||
`preventDefault()` öffnete ein Link-Knoten zusätzlich seine URL — auf Touch
|
||||
ist der Link stattdessen ein Knopf IM Fenster. */
|
||||
e.preventDefault();
|
||||
toggleNodeTip(tapped);
|
||||
toggleNodeTip(tapped, true);
|
||||
}, {passive: false});
|
||||
out.addEventListener('touchcancel', disarmPress);
|
||||
out.addEventListener('contextmenu', e => { if(pressTimer || armedEl) e.preventDefault(); });
|
||||
@@ -1605,14 +1614,14 @@ function closeNodeTip(){
|
||||
nodeTip.hidden = true;
|
||||
}
|
||||
|
||||
function toggleNodeTip(el){
|
||||
function toggleNodeTip(el, touch){
|
||||
if(tipNode === el){ closeNodeTip(); return; } /* zweiter Tipp schließt */
|
||||
showNodeTip(el, true);
|
||||
showNodeTip(el, touch);
|
||||
}
|
||||
|
||||
/* `touch` unterscheidet die beiden Anlässe: Am Zeiger ist der ganze Knoten der
|
||||
Link (§6), ein ↗-Knopf wäre dort ein zweiter Weg zum selben Ziel; und der
|
||||
Sprung-Hinweis nennt Alt+Klick, den es auf dem Telefon nicht gibt. */
|
||||
/* `touch` wählt nur noch den Sprung-Hinweis (langer Druck statt Alt+Klick).
|
||||
Der ↗-Knopf steht seit D92 an JEDEM verlinkten Knoten: Der einfache Klick
|
||||
öffnet überall das Fenster, nicht mehr die URL — wie der Tipp auf Touch. */
|
||||
function showNodeTip(el, touch){
|
||||
closeNodeTip();
|
||||
const title = el.getAttribute('data-tip') || '';
|
||||
@@ -1621,7 +1630,7 @@ function showNodeTip(el, touch){
|
||||
const desc = i >= 0 ? title.slice(0, i) : '';
|
||||
const roh = i >= 0 ? title.slice(i + sep.length) : title;
|
||||
const facts = touch ? roh.replace(t('jumpHint'), t('jumpHintTouch')) : roh;
|
||||
const href = touch && el.tagName === 'A' ? el.getAttribute('href') : null;
|
||||
const href = el.tagName === 'A' ? el.getAttribute('href') : null;
|
||||
/* Absätze: Leerzeilen trennen (SPEC §1), einzelne Zeilenumbrüche sind bloß
|
||||
der Umbruch der Quelldatei und werden zu Leerzeichen. Der `title` kann das
|
||||
nicht — er zeigt die harten Umbrüche und sah im schmalen Fenster
|
||||
@@ -1661,49 +1670,41 @@ function placeNodeTip(el){
|
||||
Math.max(12, Math.min(anchorX - left, w - 12)).toFixed(1) + 'px');
|
||||
}
|
||||
|
||||
/* ---------- Dasselbe Fenster am Zeiger und an der Tastatur (D57) ----------
|
||||
/* ---------- Dasselbe Fenster am Klick und an der Tastatur (D57, D92) ----------
|
||||
Es löst den nativen `title` ab: Der konnte weder Absätze noch eine Linie
|
||||
(der Trennstrich war aus 24 `─` gemalt, D40-Nachtrag) und erschien nie beim
|
||||
Tastaturfokus. Beides kann das Fenster ohnehin schon — es wurde nur für
|
||||
Touch gebaut (D52).
|
||||
|
||||
Die Verzögerung ist Absicht-Erkennung: Über einen dichten Baum fährt man
|
||||
hinweg, ohne etwas wissen zu wollen. Beim Wechsel von Knoten zu Knoten
|
||||
zeigt es sofort weiter — wer schon liest, wartet nicht noch einmal. */
|
||||
const TIP_DELAY = 350;
|
||||
let tipTimer = null, tipLeave = null;
|
||||
const finePointer = () => window.matchMedia('(hover:hover) and (pointer:fine)').matches;
|
||||
function cancelTipTimers(){
|
||||
clearTimeout(tipTimer); clearTimeout(tipLeave);
|
||||
tipTimer = tipLeave = null;
|
||||
Tastaturfokus. Der Hover-Weg aus D57 ist ausgebaut (D92): Mit den
|
||||
Taiga-Aktionen wurde das Fenster gewichtiger, und beim bloßen Ansteuern des
|
||||
Falt-Chips stand es schon im Weg. Der einfache Klick öffnet es jetzt
|
||||
überall — wie der Tipp auf Touch (D52); der Link eines Knotens ist damit
|
||||
auch am Zeiger der ↗-Knopf IM Fenster (Strg+Klick und Enter behalten den
|
||||
Browser-Default und öffnen die URL direkt). */
|
||||
out.addEventListener('click', e => {
|
||||
if(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
|
||||
if(e.detail === 0) return; /* Enter am Link-Knoten bleibt der Link (D25) */
|
||||
if(e.target && e.target.closest && e.target.closest('.fold')) return;
|
||||
const el = nodeFromEvent(e);
|
||||
if(!el) return;
|
||||
e.preventDefault(); /* der Klick gehört dem Fenster, nicht der URL */
|
||||
toggleNodeTip(el, false);
|
||||
});
|
||||
/* Tastaturfokus öffnet weiter sofort (D57) — aber nur echten
|
||||
(`:focus-visible`): Auch der Mausklick fokussiert den Knoten, und dann
|
||||
entschiede sonst der focusin statt des Klick-Handlers — der Toggle darüber
|
||||
schlösse das eben geöffnete Fenster sofort wieder. */
|
||||
function kbFocus(el){
|
||||
try{ return el.matches(':focus-visible'); }catch(_){ return false; }
|
||||
}
|
||||
out.addEventListener('pointerover', e => {
|
||||
if(e.pointerType === 'touch' || !finePointer()) return;
|
||||
const el = e.target.closest && e.target.closest('.node');
|
||||
if(!el || el === tipNode) return;
|
||||
cancelTipTimers();
|
||||
/* Steht schon eines offen, ist die Absicht erwiesen — dann ohne Warten. */
|
||||
tipTimer = setTimeout(() => showNodeTip(el, false), tipNode ? 0 : TIP_DELAY);
|
||||
});
|
||||
out.addEventListener('pointerout', e => {
|
||||
if(e.pointerType === 'touch' || !finePointer()) return;
|
||||
clearTimeout(tipTimer);
|
||||
/* Nicht sofort zumachen: Der Weg ins Fenster führt über den Zwischenraum,
|
||||
und dort ist der Zeiger kurz über keinem von beiden. */
|
||||
clearTimeout(tipLeave);
|
||||
tipLeave = setTimeout(() => {
|
||||
if(!nodeTip.matches(':hover')) closeNodeTip();
|
||||
}, 120);
|
||||
});
|
||||
nodeTip.addEventListener('pointerenter', cancelTipTimers);
|
||||
nodeTip.addEventListener('pointerleave', () => { closeNodeTip(); });
|
||||
/* Tastaturfokus: ohne Verzögerung — er ist bereits die ausdrückliche Absicht,
|
||||
und ein `title` hat hier noch nie etwas gezeigt. */
|
||||
out.addEventListener('focusin', e => {
|
||||
if(suppressTipFocus) return; /* Fokus-Rückgabe nach dem Falten (D38/D92) */
|
||||
const el = e.target.closest && e.target.closest('.node');
|
||||
if(el && el !== tipNode) showNodeTip(el, false);
|
||||
if(el && el !== tipNode && kbFocus(el)) showNodeTip(el, false);
|
||||
});
|
||||
out.addEventListener('focusout', e => {
|
||||
/* Fokus INS Fenster (die Knöpfe darin sind klick-fokussierbar) schließt
|
||||
nicht — sonst verschwände der Knopf zwischen mousedown und mouseup und
|
||||
sein Klick ginge verloren. */
|
||||
if(e.relatedTarget && nodeTip.contains(e.relatedTarget)) return;
|
||||
const el = e.target.closest && e.target.closest('.node');
|
||||
if(el && el === tipNode) closeNodeTip();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user