Files
DOSSIER-STANDALONE/src/panels/types.ts
T
karim 68a0459d0e Schnittebenen 2D↔3D Phase 1+2, native Fenster, Hell/Dunkel-Theme, SWISSIMAGE-Import
- Schnittebenen: 3D-Live-Schnitt folgt der gewählten Grundriss-Schnittlinie
  (section3dCutId/sectionPlaneFromLevel), Schalter "Im 3D schneiden" im
  Objekt-Info, Doppelklick auf Schnittlinie springt in 2D-Schnittansicht,
  unsichtbare Ebenen blenden ihre Führungslinie aus
- Eigene native Tauri-Fenster für Kontext-Import/Zeichnungsebenen/
  Ebenen-Einstellungen/Ressourcen/Einstellungen + klassische Menüleiste
  (AppMenuBar) neben der Wortmarke
- Hell/Dunkel-Umschalter (Einstellungen → Darstellung), persistiert,
  flackerfrei vor erstem Render gesetzt
- SWISSIMAGE-Luftbild-Import (swisstopo WMS) als Kontext-Hintergrundebene
- UI-Politur: Werkzeug-Panel Symbole/Liste umschaltbar, Topbar-Quick-Access-
  Icons entfernt, Zahnrad→Einstellungen in Panel-Köpfen, Footerbar/
  Snap-Marker/Maß-HUD auf helle Pillen-Sprache umgestellt
- Neues Dachziegel-Material (RoofingTiles013A)
2026-07-20 10:51:37 +02:00

183 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Panel-System — Kerntypen.
//
// Das Panel-System ist der erweiterbare Andockrahmen der App: links und rechts
// je ein Dock mit Tabs, jeder Tab ist ein registriertes Panel. Plugins/eigene
// Panels registrieren sich über die Registry (registry.ts) und tauchen dann in
// den Docks auf — der Rahmen kennt keine konkreten Panels.
//
// Bezeichner englisch, Kommentare/UI-Text deutsch (CONVENTIONS.md). Dieses Modul ist
// rein typdeklarativ plus der React-Context-Träger; es hat keine Laufzeitlogik
// außer createContext und ist daher SSR-/test-sicher.
import { createContext } from "react";
import type { ReactNode } from "react";
// ── Dock-Identität & Layout ────────────────────────────────────────────────
/** Welches der beiden Docks (links/rechts) gemeint ist. */
export type DockId = "left" | "right";
/**
* Eine vertikal gestapelte Gruppe innerhalb eines Docks: ein eigener
* Tab-Stapel mit eigenem aktivem Tab. Mehrere Gruppen übereinander ergeben das
* Vectorworks-Bild (z. B. links Werkzeuge OBEN, Attribute UNTEN). Jede Gruppe
* trägt ein Höhen-Gewicht (relativer flex-Anteil im Dock); die Splitter
* zwischen den Gruppen verschieben diese Gewichte.
*/
export interface DockGroup {
/** Geordnete Liste der Panel-IDs in dieser Gruppe (Tab-Reihenfolge). */
tabs: string[];
/** Aktiver Tab (Panel-ID) oder `null`, wenn die Gruppe leer ist. */
activeTab: string | null;
/**
* Relatives Höhen-Gewicht der Gruppe im Dock (flex-grow-Anteil). Nur die
* Verhältnisse zählen; ein Splitter-Zug verteilt Gewicht zwischen Nachbarn.
*/
weight: number;
}
/**
* Zustand eines einzelnen Docks: eine geordnete Liste vertikal gestapelter
* Gruppen (oben → unten) plus die Dock-Breite. Ein leeres Dock hat `groups: []`
* und wird nicht gerendert (die Mitte nutzt den Platz).
*/
export interface DockState {
/** Vertikal gestapelte Gruppen (oben → unten). */
groups: DockGroup[];
/**
* Breite des Docks in CSS-Pixeln. Persistiert, damit die Aufteilung über
* Sitzungen erhalten bleibt.
*/
size: number;
}
/**
* Ein schwebendes (frei positioniertes) Panel: weder im linken noch im rechten
* Dock, sondern als eigenes Fenster über der Mitte. Position/Größe sind
* CSS-Pixel relativ zum Anwendungsbereich; `z` ist die Stapelreihenfolge
* (höher = weiter vorne), damit zuletzt fokussierte Fenster oben liegen.
*/
export interface FloatingPanel {
/** Panel-ID (Registry-Schlüssel) — wie in DockState.tabs eindeutig. */
panelId: string;
/** Linke Kante in CSS-Pixeln (relativ zum Anwendungsbereich). */
x: number;
/** Obere Kante in CSS-Pixeln (relativ zum Anwendungsbereich). */
y: number;
/** Breite in CSS-Pixeln. */
w: number;
/** Höhe in CSS-Pixeln. */
h: number;
/** Stapelindex (höher = weiter vorne). */
z: number;
}
/**
* Gesamter Layout-Zustand des Panel-Rahmens: beide Docks, die Liste der
* schwebenden Panels plus eine Schema-Version (für Migrationen beim Laden aus
* localStorage).
*
* INVARIANTE: Jede registrierte Panel-ID erscheint an GENAU EINER Stelle —
* in einer Gruppe des linken Docks, in einer Gruppe des rechten Docks oder in
* `floating` (als `panelId`). Die Helfer in layout.ts wahren diese Invariante
* (sie entfernen ein Panel überall, bevor sie es am Ziel einfügen, und werfen
* leer gewordene Gruppen weg).
*/
export interface LayoutState {
left: DockState;
right: DockState;
/** Frei positionierte Panels (nicht in einem Dock). */
floating: FloatingPanel[];
/** Schema-Version des Layouts (für künftige Migrationen). */
version: number;
}
// ── Darstellungsmodus ──────────────────────────────────────────────────────
/**
* Darstellungsmodus für ortsabhängige Inhalte (Navigator-Listen, 3D, Grundriss).
* Steuert, welche Elemente bezogen auf die aktive Auswahl gezeigt werden — die
* fünf DOSSIER-Modi (siehe docs/design/context-menu.md) plus eine eigene
* Ergänzung ("locked"), für Ebenen UND Zeichnungsebenen identisch:
* • "all_force" — alle erzwungen sichtbar; Augen gedimmt; Klick aufs Auge
* wechselt zu „Ausgewählte".
* • "all" — sichtbar nach per-Zeile-Flag (Standard).
* • "active" — nur das aktive Element; andere stark gedimmt.
* • "grey" — aktives normal, andere 45 % (Sichtbarkeits-Flags gelten).
* • "grey_locked" — wie „grey", andere zusätzlich gesperrt.
* • "locked" — aktives normal, andere sichtbare UNVERÄNDERT farbig,
* aber gesperrt (nicht anwählbar/bearbeitbar).
*/
export type DisplayMode = "all_force" | "all" | "active" | "grey" | "grey_locked" | "locked";
// ── Panel-Definition & Kontext ─────────────────────────────────────────────
/**
* Was ein Panel beim Rendern erhält. Bewusst generisch gehalten: konkrete
* App-Daten (Projekt, Handler, aktive Auswahl) reicht der Host stattdessen über
* den React-Context `PanelHostContext` durch — so bleiben Panels von der
* Props-Signatur des Hosts entkoppelt und Plugins müssen diesen Typ nicht
* kennen. Die Index-Signatur erlaubt es, bei Bedarf trotzdem ad-hoc-Werte
* mitzugeben, ohne den Typ zu brechen.
*/
export interface PanelContext {
[key: string]: unknown;
}
/**
* Definition eines Panels — die Einheit, die in der Registry registriert und in
* einem Dock als Tab dargestellt wird.
*/
export interface PanelDef {
/** Stabile, eindeutige ID (Registry-Schlüssel, in Layouts persistiert). */
id: string;
/**
* Im Tab angezeigter Titel — als i18n-Key (z. B. „nav.layers"). Der Rahmen
* (TabStrip/PanelFrame) löst ihn über `t()` auf, sodass der Titel der
* gewählten Sprache folgt. Ist der String kein bekannter Key, gibt `t()` ihn
* unverändert zurück (Plugins können also auch einen fertigen Text setzen).
*/
title: string;
/**
* Symbol für den Tab in der TabStrip (der Titeltext erscheint dort nur noch
* als Tooltip). Erwartet eine kleine Inline-SVG (~16×16, stroke=currentColor),
* damit sie die Textfarbe des Tabs (inaktiv/hover/aktiv) übernimmt. Fehlt es
* (z. B. bei einem Plugin-Panel ohne eigenes Icon), fällt die TabStrip auf den
* Titeltext zurück.
*/
icon?: ReactNode;
/** Rendert den Panel-Inhalt. Erhält den (generischen) PanelContext. */
render: (ctx: PanelContext) => ReactNode;
/**
* Ob das Panel den Darstellungsmodus-Umschalter (DisplayMode) in seiner
* Kopfzeile anbieten soll. Default: kein Umschalter.
*/
hasDisplayMode?: boolean;
/**
* Optionale Aktions-Elemente rechts in der EINEN Kopfzeile des PanelFrame
* (z. B. „+ Kategorie", Fenster-Ausklinken). So bündeln Panels ihre Aktionen
* in der gemeinsamen Frame-Kopfzeile statt einer zweiten eigenen Titelzeile
* (kein doppelter Titel mehr). Wird INNERHALB des PanelHostContext gerendert,
* darf also `usePanelHost()` nutzen.
*/
headerActions?: ReactNode;
}
// ── Host-Context (App-Zustand + Handler für Panels) ────────────────────────
/**
* Träger des App-Zustands und der Mutations-Handler, den die App über einen
* Provider bereitstellt und den Panels per `useContext(PanelHostContext)`
* abgreifen. Bewusst lose typisiert (Record), damit dieses Kernmodul nicht vom
* konkreten Projekt-/Handler-Modell abhängt und Plugins frei darauf zugreifen
* können. Die App liefert hier u. a. `project`, die aktive Auswahl, die
* Ressourcen-Handler und die Darstellungsmodi hinein.
*
* `null` bedeutet „außerhalb eines Providers gerendert" — Consumer sollten das
* abfangen (siehe usePanelHost in einem späteren Schritt).
*/
export type PanelHost = Record<string, unknown>;
/** React-Context, über den der Host seinen Zustand an Panels durchreicht. */
export const PanelHostContext = createContext<PanelHost | null>(null);