From 2b6b99059ddea873aa8f698f533664b49de30873 Mon Sep 17 00:00:00 2001 From: Karim Date: Fri, 3 Jul 2026 20:08:03 +0200 Subject: [PATCH] Fang-Optionen und Display/Print-Umschalter in die Footerbar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Linien-Modus (Display/Print) wandert aus der Oberleiste und die Fang-Optionen aus der Werkzeug-Palette in die Statusleiste unten: - StatusBar: segmentierter Display/Print-Umschalter (Stil wie der Renderer-Umschalter) plus Fang-Pill mit nach oben öffnendem Popover (Master-Schalter + Endpunkt/Mittelpunkt/Schnittpunkt/Kante/Raster/ Ortho + Rastermass). - TopBar: Linien-Modus-Gruppe entfernt. - ToolsPanel: Fang-Abschnitt entfernt. - App reicht lineMode/snap an die StatusBar durch. --- src/App.tsx | 8 +- src/panels/ToolsPanel.tsx | 86 ---------------- src/styles.css | 91 ++++++++++++++++ src/ui/StatusBar.tsx | 211 +++++++++++++++++++++++++++++++++++++- src/ui/TopBar.tsx | 33 ------ 5 files changed, 306 insertions(+), 123 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 6af317f..05c6ed8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2715,9 +2715,6 @@ export default function App() { referenceLines={referenceLines} onReferenceLines={setReferenceLines} referenceLinesEnabled={planActive} - lineMode={lineMode} - onLineMode={setLineMode} - lineModeEnabled={planActive} combos={{ // Ebenen-Kombinationen: Speichern = Snapshot des Layer-Baums → // localStorage; Laden = Map lesen → Sichtbarkeits-Flags anwenden. @@ -2959,6 +2956,11 @@ export default function App() { rendererMode={rendererMode} webGpuAvailable={webGpuAvailable} onRendererModeChange={setRendererMode} + lineMode={lineMode} + onLineModeChange={setLineMode} + lineModeEnabled={planActive} + snap={snap} + onSnapChange={setSnap} /> {/* Schwebendes Ressourcen-Fenster ÜBER allem (hoher z-index, abgedunkelter diff --git a/src/panels/ToolsPanel.tsx b/src/panels/ToolsPanel.tsx index a644021..a21a197 100644 --- a/src/panels/ToolsPanel.tsx +++ b/src/panels/ToolsPanel.tsx @@ -119,7 +119,6 @@ function ToolIcon({ id }: { id: ToolId }) { export function ToolsPanel() { const host = usePanelHost(); - const snap = host.snap; return (
@@ -190,91 +189,6 @@ export function ToolsPanel() { )} {host.activeTool === "ceiling" &&
} - - {/* Fang-Abschnitt. Master-Checkbox steuert die übrigen Fang-Optionen. */} - - - - - - - - - - - - - - -
); } diff --git a/src/styles.css b/src/styles.css index 916d786..85dd895 100644 --- a/src/styles.css +++ b/src/styles.css @@ -803,6 +803,97 @@ body { cursor: default; } +/* Generische Segment-Pille für die Statusleiste (Linien-Darstellung, Fang) — + gleiche Masse/Optik wie .sb-renderer-toggle, aber nicht renderer-spezifisch. */ +.sb-seg { + display: inline-flex; + align-items: stretch; + height: 16px; + border: 1px solid var(--border); + border-radius: 999px; + overflow: hidden; + background: var(--input); +} +.sb-seg.disabled { + opacity: 0.4; +} +.sb-seg-btn { + display: inline-flex; + align-items: center; + border: none; + border-left: 1px solid var(--border); + background: transparent; + color: var(--muted); + font-family: inherit; + font-size: 10px; + font-weight: 600; + letter-spacing: 0.02em; + padding: 0 8px; + cursor: pointer; + transition: background 0.14s, color 0.14s; +} +.sb-seg-btn:first-child { + border-left: none; +} +.sb-seg-btn:hover:not(:disabled) { + background: var(--accent-dim); + color: var(--accent); +} +.sb-seg-btn.active { + background: var(--accent); + color: #fff; +} +.sb-seg-btn:disabled { + opacity: 0.4; + cursor: default; +} +/* Caret-Segment (öffnet das Fang-Popover) — schmaler als die Text-Segmente. */ +.sb-seg-caret { + padding: 0 6px; + font-size: 9px; + line-height: 1; +} + +/* Fang-Kontrolle: Wrapper ist relativ positioniert, damit das Popover NACH + OBEN öffnen kann (die Leiste sitzt ganz unten im App-Shell). */ +.sb-snap-wrap { + position: relative; + display: inline-flex; +} +.sb-snap-popover { + position: absolute; + right: 0; + bottom: 100%; + margin: 0 0 6px; + padding: 6px 0; + min-width: 170px; + background: var(--panel, #1c2128); + border: 1px solid var(--border); + border-radius: 8px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45); + z-index: 200; +} +.sb-snap-row { + display: flex; + align-items: center; + gap: 8px; + padding: 3px 12px; + font-size: 12px; + cursor: pointer; + user-select: none; + white-space: nowrap; +} +.sb-snap-row input[type="checkbox"] { + accent-color: var(--accent); +} +.sb-snap-row-num { + justify-content: space-between; + cursor: default; +} +.sb-snap-row-num input[type="number"] { + width: 64px; +} + /* ── Command-Line (Rhino-artig) — über der Statusleiste ────────────────────── Dunkle, ruhige Leiste mit Prompt links, klickbaren Inline-Optionen und einer monospace-Texteingabe rechts; das Autocomplete-Dropdown öffnet nach oben. */ diff --git a/src/ui/StatusBar.tsx b/src/ui/StatusBar.tsx index b247960..8f418d9 100644 --- a/src/ui/StatusBar.tsx +++ b/src/ui/StatusBar.tsx @@ -8,7 +8,9 @@ // Rein gesteuert (controlled): alle Werte kommen aus App (Modell + View-State), // keine eigene Logik. Bezeichner englisch, UI-Text/Kommentare deutsch. +import { useEffect, useRef, useState } from "react"; import type { Vec2 } from "../model/types"; +import type { SnapSettings } from "../tools/types"; import { t } from "../i18n"; /** Plan-Renderer-Wahl, gespiegelt aus dem `?engine`-URL-Param (siehe PlanView). */ @@ -41,6 +43,199 @@ export interface StatusBarProps { * Live-Remount der Renderer-Hooks). */ onRendererModeChange: (mode: RendererMode) => void; + /** Linien-Darstellung: „display" (Haarlinien) oder „print" (echte mm-Stärken). */ + lineMode: "display" | "print"; + /** Linien-Darstellung wechseln. */ + onLineModeChange: (mode: "display" | "print") => void; + /** Nur im Grundriss sinnvoll — deaktiviert beide Segmente wenn `false`. */ + lineModeEnabled: boolean; + /** Fang-Einstellungen (Werkzeug-übergreifend). */ + snap: SnapSettings; + /** Fang-Einstellungen ändern. */ + onSnapChange: (s: SnapSettings) => void; +} + +/** + * Zweisegmentige Umschalt-Pille Display/Print für die Statusleiste — optisch + * an `.sb-renderer-toggle` angelehnt (gleiche Höhe/Radius), eigene Klasse + * `.sb-seg` weil sie generisch (nicht renderer-spezifisch) bleibt. + */ +function LineModeToggle({ + lineMode, + onLineModeChange, + lineModeEnabled, +}: { + lineMode: "display" | "print"; + onLineModeChange: (m: "display" | "print") => void; + lineModeEnabled: boolean; +}) { + return ( + + {t("topbar.lineModeGroup")} + + + + + + ); +} + +/** + * Fang-Kontrolle für die Statusleiste: eine Pille schaltet `snap.enabled` + * direkt um (Klick = an/aus, aktive Einfärbung wie der Renderer-Umschalter); + * ein kleiner Caret daneben öffnet ein NACH OBEN öffnendes Popover (Leiste + * sitzt unten) mit den Fang-Unteroptionen — 1:1 aus dem früheren + * ToolsPanel-„Fang-Abschnitt" übernommen. Schliesst bei Klick ausserhalb. + */ +function SnapControl({ + snap, + onSnapChange, +}: { + snap: SnapSettings; + onSnapChange: (s: SnapSettings) => void; +}) { + const [open, setOpen] = useState(false); + const wrapRef = useRef(null); + + useEffect(() => { + if (!open) return; + const onDown = (e: MouseEvent) => { + if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) { + setOpen(false); + } + }; + document.addEventListener("mousedown", onDown); + return () => document.removeEventListener("mousedown", onDown); + }, [open]); + + return ( + + + + + + {open && ( +
+ + + + + + + + +
+ )} +
+ ); } /** Ein Wert-Feld im rechten Cluster: kleine Beschriftung + Wert. */ @@ -72,6 +267,11 @@ export function StatusBar({ rendererMode, webGpuAvailable, onRendererModeChange, + lineMode, + onLineModeChange, + lineModeEnabled, + snap, + onSnapChange, }: StatusBarProps) { const coords = cursor ? `${fmt(cursor.x)}, ${fmt(cursor.y)}` @@ -82,8 +282,17 @@ export function StatusBar({ {/* Links: Werkzeug-Hinweis. */} {hint} - {/* Rechts: abgeleiteter Wert-Cluster. */} + {/* Rechts: abgeleiteter Wert-Cluster. Interaktive Kontrollen (Linien- + Darstellung, Fang) zuerst, danach die reinen Anzeigewerte. */}
+ + + + {t("status.renderer")} diff --git a/src/ui/TopBar.tsx b/src/ui/TopBar.tsx index 1c7644c..9535316 100644 --- a/src/ui/TopBar.tsx +++ b/src/ui/TopBar.tsx @@ -141,12 +141,6 @@ export interface TopBarProps { /** Referenzlinien nur im Grundriss sinnvoll (sonst deaktiviert). */ referenceLinesEnabled: boolean; - // Linien-Darstellung: Display (Haarlinien) ↔ Print (echte mm-Strichstärken). - lineMode: "display" | "print"; - onLineMode: (m: "display" | "print") => void; - /** Nur im Grundriss sinnvoll. */ - lineModeEnabled: boolean; - // Sichtbarkeits-Kombinationen (Ebenen-/Zeichnungskombinationen). /** Handler-Satz für die beiden Kombinations-Dropdowns. */ combos: ComboMenuHandlers; @@ -855,9 +849,6 @@ export function TopBar({ referenceLines, onReferenceLines, referenceLinesEnabled, - lineMode, - onLineMode, - lineModeEnabled, combos, resourcesOpen, onToggleResources, @@ -1202,30 +1193,6 @@ export function TopBar({ - {/* Linien-Darstellung: Display (alle Haarlinien) ↔ Print (echte mm- - Strichstärken). Eine Einfach-Auswahl → als Dropdown (Trigger-Pill + - Häkchen-Liste, wie Detailgrad/Massstab), nicht mehr als Segment-Pillen. - Optionen tragen ein Icon plus Klartext. Nur im Grundriss sinnvoll. */} -
- -
- {/* Rechte Gruppe: Layout-Menü · Ressourcen · Projektname. */}
{layoutMenu}