// Ressourcen-Manager — verwaltete Stil-Bibliotheken nach DOSSIER-Vorbild // (Vectorworks-Stil). Drei Sektionen in einer rechten Schublade (Drawer): // • Bauteile (Components) — Poché-/3D-Farbe, Verschneidungs-Rang, Schraffur. // • Schraffuren (Hatches) — Muster/Maßstab/Winkel/Farbe + Linienstil. // • Linien (Line Styles) — Strichstärke/Farbe/Strichmuster. // // Darstellung als saubere Tabelle (CONVENTIONS.md): pro Tab EINE sticky Kopfzeile // mit Spaltentiteln, darunter kompakte Datenzeilen mit Inline-Edit pro Zelle — // keine wiederholten Feld-Beschriftungen je Zeile. Umgesetzt als CSS-Grid, das // Kopf- und Datenzeilen dieselben Spalten teilen (`grid-template-columns` je // Tab), damit die Spalten exakt fluchten. // // Reine Darstellung + Callbacks: alle Mutationen laufen immutabel über die // vom App gereichten Handler (setProject). Plan & 3D leiten aus demselben // Projekt ab und aktualisieren dadurch live — diese Komponente kennt weder // Rendering noch Persistenz. Bezeichner englisch, UI-Text deutsch // (CONVENTIONS.md). import { useEffect, useRef, useState } from "react"; import type { Component, ComponentMaterial, HatchPattern, HatchStyle, LineStyle, Project, WallType, } from "../model/types"; import { PEN_WEIGHTS } from "../model/types"; import { parseLin } from "../io/linParser"; import { parsePat } from "../io/patParser"; import { MATERIAL_LIBRARY } from "../materials/library"; import type { MaterialAsset } from "../materials/library"; import { materialFromAsset, DEFAULT_TILE_SIZE_M } from "../materials/runtime"; import { searchMaterials, fetchMaterialMaps, AMBIENT_CATEGORIES, type AmbientMaterial, type AmbientResolution, } from "../materials/ambientcg"; import { requestMaterialPreview, cancelMaterialPreview } from "../materials/spherePreview"; import { EyeIcon } from "./EyeIcon"; import { t } from "../i18n"; // ── Auswahl-Optionen (UI-Beschriftung übersetzt, Werte englisch) ─────────── /** Schraffur-Muster mit i18n-Key für die Beschriftung. */ const PATTERN_OPTIONS: { value: HatchPattern; labelKey: string }[] = [ { value: "none", labelKey: "pattern.none" }, { value: "solid", labelKey: "pattern.solid" }, { value: "insulation", labelKey: "pattern.insulation" }, { value: "diagonal", labelKey: "pattern.diagonal" }, { value: "crosshatch", labelKey: "pattern.crosshatch" }, ]; /** * Benannte Strichmuster (mm). `null` = durchgezogen. Werte als Schlüssel * serialisiert, damit das Dropdown den aktuellen Stil wiedererkennt. */ const DASH_OPTIONS: { key: string; labelKey: string; dash: number[] | null }[] = [ { key: "solid", labelKey: "dash.solid", dash: null }, { key: "dashed", labelKey: "dash.dashed", dash: [6, 4] }, { key: "dotted", labelKey: "dash.dotted", dash: [1, 3] }, ]; /** Findet den Dash-Schlüssel, der zu einem Strichmuster passt (sonst custom). */ function dashKey(dash: number[] | null): string { const hit = DASH_OPTIONS.find( (o) => (o.dash === null && dash === null) || (o.dash !== null && dash !== null && o.dash.length === dash.length && o.dash.every((v, i) => v === dash[i])), ); return hit ? hit.key : "custom"; } // ── Wiederverwendbare Feld-Bausteine (DOSSIER-Look) ──────────────────────── // Alle Steuerelemente füllen ihre Tabellenzelle (width:100%) und werden über // die Spaltenbreite des Grids dimensioniert; Zahlenfelder sind rechtsbündig. /** Kompaktes Textfeld (Pill) für Namen/Platzhalter. */ function TextField({ value, onChange, placeholder, mono = false, }: { value: string; onChange: (v: string) => void; placeholder?: string; mono?: boolean; }) { return ( onChange(e.target.value)} /> ); } /** Zahlenfeld (Pill, rechtsbündig, monospace) mit Live-Commit. */ function NumberField({ value, onChange, step = 1, min, max, list, }: { value: number; onChange: (v: number) => void; step?: number; min?: number; max?: number; /** Optionale -ID mit Vorschlagswerten (z. B. Stiftstärken). */ list?: string; }) { return ( { const v = Number(e.target.value); if (!Number.isNaN(v)) onChange(v); }} /> ); } /** * Farb-Swatch mit nativem Color-Picker. Das