Hustler-Pixelwelt T13: Korrekturen Hochhaus (Fokus, Tastatur, weniger Bewegung, Handy-Tippflächen, Schnee per transform, Pause überall, Seite ohne Leerlauf-Blitz)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-22 09:47:57 +00:00
1 parent 0924a8d2ae
commit 5e6698f5e0
14 files changed
+994 -52

No files matched your search

@@ -3,9 +3,12 @@
// Raumschilder, Bildschirme, Radar, Briefkasten-Zähler, Tresor-/Keller-Text, den Etagen (Knöpfe, Freigabe und Keller sind Links) und den 19 Hustlern; davor der Schnee.
// Alles hängt am Dienst-Zustand: Figur und Zusätze nach Status (figurZustand), graue Raum-Ebene samt Papierschild nur für nicht angeschlossene Such-Räume.
// Die Kamera (Rad, Ziehen, Pinch, Tasten) steckt in useKamera. `children` (die Regie-Ebene) liegt in der Bühne über den Figuren, in Weltkoordinaten.
// Bedienung: Tastaturfokus (:focus-visible) auf Etage oder Figur außerhalb des Bildes holt sie per Kamera herein (beiFokus, kein Schwenk bei Mausklick); das Sichtfenster ist
// per tabindex -1 fokussierbar (ein Klick in die Welt gibt ihm den Fokus, damit + - 0 Esc wirken); die Tippflächen der Figuren wachsen mit dem Zoom (--hk-tipp-*, tippRand);
// bei offener Detailansicht (gesperrt) trägt das Sichtfenster hk-verdeckt (Schnee aus).
// Kein CSS-Import (die Stile hängt WorkersPage ein: styles/hustler/hochhaus.css, hustler-ui.css, hustler-welt.css), keine Bilder, kein innerHTML.
import { forwardRef, useImperativeHandle } from 'react'
import { anzeigeName, figurZustand, schilder } from '../../../lib/hustlerSicht.js'
import { anzeigeName, figurZustand, schilder, tippRand } from '../../../lib/hustlerSicht.js'
import { AUFZUG, ETAGEN, ETAGEN_SPALTE, HAUS_BILD, HUSTLER, RAEUME, SCHILDER } from '../../../lib/hustlerWelt.js'
import { FLOOR_TITLES, ROLES, ROOM_TITLES } from '../../../lib/workersRoster.js'
import useKamera from './useKamera.js'
@@ -20,6 +23,11 @@ const HAUS_RECHTECK = Object.freeze({ left: HAUS_BILD.x, top: HAUS_BILD.y, width
const PX = (n) => `${n}px`
// Elemente, die Fokus per Tastatur bekommen können und beim Fokus ins Bild geholt werden (M1)
const FOKUS_ZIEL = '.hk-etage, .hk-hustler'
// Namensschild einer Figur: so viele Bildschirm-Pixel über dem Knopf sollen beim Heranholen mit sichtbar sein
const SCHILD_HOEHE = 34
// Feste, je Figur verschiedene Startverschiebung der Animation (deterministisch aus dem Index: 7 und 19 sind teilerfremd, 19 Figuren -> 19 Werte)
const versatz = (index, schritt = 7, anzahl = 19) => `-${(((index * schritt) % anzahl) / 10).toFixed(1)}s`
@@ -49,6 +57,7 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
const grau = new Set(figuren.filter((x) => x.f.raumGrau).map((x) => x.f.raum))
const { s, x, y } = kam
const tipp = tippRand(s)
const etikett = Math.min(18, Math.max(8, 15 / s)).toFixed(2)
const namePx = Math.min(14, Math.max(4, 12 / s)).toFixed(2)
const buehneStil = {
@@ -56,6 +65,10 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
imageRendering: s * geraeteDichte() < 1 ? 'auto' : 'pixelated',
'--hk-etikett': `${etikett}px`,
'--hk-name': `${namePx}px`,
// Tippfläche der Figuren: wächst mit dem Zoom auf 40 Bildschirm-Pixel (M8, hustler-welt.css)
'--hk-tipp-x': `${tipp.x.toFixed(2)}px`,
'--hk-tipp-oben': `${tipp.oben.toFixed(2)}px`,
'--hk-tipp-unten': `${tipp.unten.toFixed(2)}px`,
}
const zeige = (id) => () => {
@@ -70,10 +83,29 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
if (typeof onOpen === 'function') onOpen(tab)
}
// Fokus per Tastatur (:focus-visible) auf Etage oder Figur außerhalb des sichtbaren Bereichs: Kamera holt sie ins Bild (M1). Kein Schwenk bei Mausklick.
const beiFokus = (e) => {
if (gesperrt) return
const knoten = e && e.target
if (!knoten || typeof knoten.closest !== 'function') return
const ziel = knoten.closest(FOKUS_ZIEL)
if (!ziel) return
let tastatur = false
try { tastatur = ziel.matches(':focus-visible') } catch { tastatur = false }
if (!tastatur) return
// Der Browser darf das Sichtfenster nicht selbst verschieben (alte Browser ohne overflow: clip scrollen beim Fokus)
const sicht = e.currentTarget
if (sicht && (sicht.scrollLeft || sicht.scrollTop)) { sicht.scrollLeft = 0; sicht.scrollTop = 0 }
const figur = typeof ziel.matches === 'function' && ziel.matches('.hk-hustler')
kam.zeige({ left: ziel.offsetLeft, top: ziel.offsetTop, width: ziel.offsetWidth, height: ziel.offsetHeight }, figur ? SCHILD_HOEHE : 0)
}
return (
<div
ref={kam.view}
className={`hk-sicht${kam.ziehen ? ' hk-zieht' : ''}${pausiert ? ' hk-steht' : ''}`}
className={`hk-sicht${kam.ziehen ? ' hk-zieht' : ''}${pausiert ? ' hk-steht' : ''}${gesperrt ? ' hk-verdeckt' : ''}`}
tabIndex={-1}
onFocus={beiFokus}
onPointerDown={kam.handlers.onPointerDown}
onPointerMove={kam.handlers.onPointerMove}
onPointerUp={kam.handlers.onPointerUp}
@@ -100,6 +132,7 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
<div className="hk-haus hk-schild-b" aria-hidden="true" style={HAUS_RECHTECK} />
<div
className="hk-neon"
aria-hidden="true"
style={{ left: SCHILDER.neon.left, top: SCHILDER.neon.top, width: SCHILDER.neon.w, height: SCHILDER.neon.h, fontSize: PX(SCHILDER.neon.schrift), lineHeight: PX(SCHILDER.neon.schrift) }}
>
HUSTLER
@@ -186,13 +219,13 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
const link = LINK_ETAGEN[e.id]
if (link) {
return (
<a key={e.id} className="hk-etage" href={`/hustler?tab=${link.tab}`} draggable={false} onClick={linkKlick(link.tab)} aria-label={link.label} style={stil}>
<a key={e.id} className="hk-etage" data-hk-etage={e.id} href={`/hustler?tab=${link.tab}`} draggable={false} onClick={linkKlick(link.tab)} aria-label={link.label} style={stil}>
<span className="hk-etage-name">{link.text}</span>
</a>
)
}
return (
<button key={e.id} type="button" className="hk-etage" onClick={() => kam.etage(e.oben, e.unten)} aria-label={`${FLOOR_TITLES[e.id]} heranzoomen`} style={stil}>
<button key={e.id} type="button" className="hk-etage" data-hk-etage={e.id} onClick={() => kam.etage(e.oben, e.unten)} aria-label={`${FLOOR_TITLES[e.id]} heranzoomen`} style={stil}>
<span className="hk-etage-name">{FLOOR_TITLES[e.id]}</span>
</button>
)
@@ -207,6 +240,7 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
type="button"
className="hk-hustler"
data-hk-hustler={id}
aria-haspopup="dialog"
onClick={zeige(id)}
aria-label={f.label}
style={{ left: k.left, top: k.top, width: k.w, height: k.h }}