diff --git a/src/App.tsx b/src/App.tsx index 8b711dd..89709b7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -39,6 +39,7 @@ import type { OverrideRule, Project, Roof, + RoofType, Stair, StairType, Vec2, @@ -2273,6 +2274,33 @@ export default function App() { ceilingTypes: (p.ceilingTypes ?? []).filter((ct) => ct.id !== id), }; }); + // ── Dachtypen (Dachaufbauten) — analog Deckentyp ────────────────────────── + const patchRoofType = (id: string, patch: Partial) => + setProject((p) => ({ + ...p, + roofTypes: (p.roofTypes ?? []).map((rt) => (rt.id === id ? { ...rt, ...patch } : rt)), + })); + const addRoofType = () => + setProject((p) => { + const componentId = p.components[0]?.id ?? ""; + const roofType: RoofType = { + id: `rt-${Date.now()}`, + name: t("default.roofTypeName"), + layers: [{ componentId, thickness: 0.2 }], + }; + return { ...p, roofTypes: [...(p.roofTypes ?? []), roofType] }; + }); + // Dachtyp löschen — geschützt, solange ihn ein Dach verwendet. + const deleteRoofType = (id: string) => + setProject((p) => { + const used = (p.roofs ?? []).some((r) => r.roofTypeId === id); + if (used) { + const name = (p.roofTypes ?? []).find((rt) => rt.id === id)?.name ?? id; + notify(t("alert.roofTypeInUse", { name }), "warning"); + return p; + } + return { ...p, roofTypes: (p.roofTypes ?? []).filter((rt) => rt.id !== id) }; + }); // ── Bauteil-Typen: Tür / Fenster / Treppe (Bibliothek) ──────────────────── // Anlegen/Patchen/Löschen analog Wand-/Deckentyp — direkt über setProject // (kein eigener Slice). Löschen ist geschützt, solange ein Element den Typ @@ -2954,6 +2982,9 @@ export default function App() { onPatchCeilingType: patchCeilingType, onAddCeilingType: addCeilingType, onDeleteCeilingType: deleteCeilingType, + onPatchRoofType: patchRoofType, + onAddRoofType: addRoofType, + onDeleteRoofType: deleteRoofType, onImportLineStyles: importLineStyles, onImportHatches: importHatches, selection, @@ -3941,6 +3972,9 @@ export default function App() { onPatchCeilingType: patchCeilingType, onAddCeilingType: addCeilingType, onDeleteCeilingType: deleteCeilingType, + onPatchRoofType: patchRoofType, + onAddRoofType: addRoofType, + onDeleteRoofType: deleteRoofType, onAddDoorType: addDoorType, onPatchDoorType: patchDoorType, onDeleteDoorType: deleteDoorType, diff --git a/src/i18n/de.ts b/src/i18n/de.ts index c6ddf8a..8e1c3cf 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -638,6 +638,7 @@ export const de = { "default.ceilingTypeSingle": "Decke {thickness} m", "default.wallTypeName": "Neuer Wandtyp", "default.ceilingTypeName": "Neuer Deckentyp", + "default.roofTypeName": "Neuer Dachtyp", "default.doorTypeName": "Neuer Türtyp", "default.windowTypeName": "Neuer Fenstertyp", "default.stairTypeName": "Neuer Treppentyp", @@ -649,6 +650,7 @@ export const de = { "alert.lineStyleInUse": "Linienstil „{name}\" wird von einer Schraffur verwendet und kann nicht gelöscht werden.", "alert.wallTypeInUse": "Wandtyp „{name}\" wird von einer Wand verwendet und kann nicht gelöscht werden.", "alert.ceilingTypeInUse": "Deckentyp „{name}\" wird von einer Decke verwendet und kann nicht gelöscht werden.", + "alert.roofTypeInUse": "Dachtyp „{name}\" wird von einem Dach verwendet und kann nicht gelöscht werden.", "alert.doorTypeInUse": "Türtyp „{name}\" wird von einer Tür verwendet und kann nicht gelöscht werden.", "alert.windowTypeInUse": "Fenstertyp „{name}\" wird von einem Fenster verwendet und kann nicht gelöscht werden.", "alert.stairTypeInUse": "Treppentyp „{name}\" wird von einer Treppe verwendet und kann nicht gelöscht werden.", @@ -662,6 +664,7 @@ export const de = { "resources.tab.lines": "Linien", "resources.tab.wallStyles": "Wandstile", "resources.tab.ceilingStyles": "Deckenstile", + "resources.tab.roofStyles": "Dachtypen", "resources.tab.doorStyles": "Türtypen", "resources.tab.windowStyles": "Fenstertypen", "resources.tab.stairStyles": "Treppentypen", @@ -703,6 +706,10 @@ export const de = { "resources.ceilingStyles.empty": "Noch keine Deckentypen.", "resources.wallStyles.placeholder": "Wandtyp", "resources.ceilingStyles.placeholder": "Deckentyp", + "resources.roofStyles.hint": + "Je Dachtyp (Schichtaufbau aussen/Eindeckung → innen/Verkleidung) die Schichtfugen als wählbaren Linienstil. Ohne Zuweisung: Haarlinie 0.02 mm.", + "resources.roofStyles.empty": "Noch keine Dachtypen.", + "resources.roofStyles.placeholder": "Dachtyp", // Bauteil-Typen (Tür/Fenster/Treppe) — Formular-Bibliotheken. "resources.doorTypes.hint": @@ -878,6 +885,7 @@ export const de = { "resources.delete.hatch": "Schraffur „{name}\" löschen", "resources.delete.wallType": "Wandtyp „{name}\" löschen", "resources.delete.ceilingType": "Deckentyp „{name}\" löschen", + "resources.delete.roofType": "Dachtyp „{name}\" löschen", // Schraffur-/Linien-Typ (Master-Detail-Editor). "resources.field.type": "Typ", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 7f6c69f..fd5eef2 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -633,6 +633,7 @@ export const en: Record = { "default.ceilingTypeSingle": "Ceiling {thickness} m", "default.wallTypeName": "New wall type", "default.ceilingTypeName": "New ceiling type", + "default.roofTypeName": "New roof type", "default.doorTypeName": "New door type", "default.windowTypeName": "New window type", "default.stairTypeName": "New stair type", @@ -644,6 +645,7 @@ export const en: Record = { "alert.lineStyleInUse": "Line style “{name}” is used by a hatch and cannot be deleted.", "alert.wallTypeInUse": "Wall type “{name}” is used by a wall and cannot be deleted.", "alert.ceilingTypeInUse": "Ceiling type “{name}” is used by a ceiling and cannot be deleted.", + "alert.roofTypeInUse": "Roof type “{name}” is used by a roof and cannot be deleted.", "alert.doorTypeInUse": "Door type “{name}” is used by a door and cannot be deleted.", "alert.windowTypeInUse": "Window type “{name}” is used by a window and cannot be deleted.", "alert.stairTypeInUse": "Stair type “{name}” is used by a stair and cannot be deleted.", @@ -657,6 +659,7 @@ export const en: Record = { "resources.tab.lines": "Lines", "resources.tab.wallStyles": "Wall styles", "resources.tab.ceilingStyles": "Ceiling styles", + "resources.tab.roofStyles": "Roof types", "resources.tab.doorStyles": "Door types", "resources.tab.windowStyles": "Window types", "resources.tab.stairStyles": "Stair types", @@ -698,6 +701,10 @@ export const en: Record = { "resources.ceilingStyles.empty": "No ceiling types yet.", "resources.wallStyles.placeholder": "Wall type", "resources.ceilingStyles.placeholder": "Ceiling type", + "resources.roofStyles.hint": + "Per roof type (layer build-up outside/covering → inside/lining) the layer joints as a selectable line style. Without assignment: hairline 0.02 mm.", + "resources.roofStyles.empty": "No roof types yet.", + "resources.roofStyles.placeholder": "Roof type", // Component types (door/window/stair) — form-based libraries. "resources.doorTypes.hint": @@ -871,6 +878,7 @@ export const en: Record = { "resources.delete.hatch": "Delete hatch “{name}”", "resources.delete.wallType": "Delete wall type “{name}”", "resources.delete.ceilingType": "Delete ceiling type “{name}”", + "resources.delete.roofType": "Delete roof type “{name}”", // Hatch/line type (master-detail editor). "resources.field.type": "Type", diff --git a/src/panels/ResourcesPanel.tsx b/src/panels/ResourcesPanel.tsx index a7d2da6..88e3975 100644 --- a/src/panels/ResourcesPanel.tsx +++ b/src/panels/ResourcesPanel.tsx @@ -38,6 +38,9 @@ export function ResourcesPanel() { onAddWallType: host.onAddWallType, onDeleteWallType: host.onDeleteWallType, onPatchCeilingType: host.onPatchCeilingType, + onPatchRoofType: host.onPatchRoofType, + onAddRoofType: host.onAddRoofType, + onDeleteRoofType: host.onDeleteRoofType, onAddCeilingType: host.onAddCeilingType, onDeleteCeilingType: host.onDeleteCeilingType, onImportLineStyles: host.onImportLineStyles, diff --git a/src/panels/host.ts b/src/panels/host.ts index 4372cd9..fd7423a 100644 --- a/src/panels/host.ts +++ b/src/panels/host.ts @@ -17,6 +17,7 @@ import type { DisplayMode } from "./types"; import type { AttributeSource, CeilingType, + RoofType, Component, ContextObject, DrawingLevel, @@ -132,6 +133,10 @@ export interface PanelHostValue { onPatchCeilingType: (id: string, patch: Partial) => void; onAddCeilingType: () => void; onDeleteCeilingType: (id: string) => void; + /** Dachtypen (Dachaufbauten): immutable Änderung/Anlage/Löschung. */ + onPatchRoofType: (id: string, patch: Partial) => void; + onAddRoofType: () => void; + onDeleteRoofType: (id: string) => void; /** Import fertiger (id-loser) Linienstile/Schraffuren (.lin/.pat). */ onImportLineStyles: (styles: Omit[]) => void; onImportHatches: (hatches: Omit[]) => void; diff --git a/src/ui/ResourceManager.tsx b/src/ui/ResourceManager.tsx index 27a06e6..312463c 100644 --- a/src/ui/ResourceManager.tsx +++ b/src/ui/ResourceManager.tsx @@ -20,6 +20,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import type { PointerEvent as ReactPointerEvent } from "react"; import type { CeilingType, + RoofType, Component, ComponentMaterial, DoorType, @@ -1893,6 +1894,39 @@ function CeilingStylesTab({ ); } +/** + * Tab „Dachtypen" — das geneigte Gegenstück zu {@link CeilingStylesTab}: gleicher + * {@link LayeredStylesTab}-Rumpf (Schichtaufbau aussen/Eindeckung → innen/ + * Verkleidung), Ressource `roofTypes`. Vorschau in Decken-Orientierung + * (gestapelt) — für die Schichtreihenfolge/-dicken ausreichend. + */ +function RoofStylesTab({ + project, + onPatchRoofType, + onAddRoofType, + onDeleteRoofType, +}: { + project: Project; + onPatchRoofType: (id: string, patch: Partial) => void; + onAddRoofType: () => void; + onDeleteRoofType: (id: string) => void; +}) { + return ( + + ); +} + /** * Datei-Import-Schaltfläche (verstecktes ). Liest die gewählte * Datei als Text und reicht ihn nach oben. Für .lin/.pat-Import. @@ -2957,6 +2991,7 @@ export type TabId = | "lines" | "wallStyles" | "ceilingStyles" + | "roofStyles" | "doorStyles" | "windowStyles" | "stairStyles" @@ -2969,6 +3004,7 @@ const TABS: { id: TabId; labelKey: string }[] = [ { id: "lines", labelKey: "resources.tab.lines" }, { id: "wallStyles", labelKey: "resources.tab.wallStyles" }, { id: "ceilingStyles", labelKey: "resources.tab.ceilingStyles" }, + { id: "roofStyles", labelKey: "resources.tab.roofStyles" }, { id: "doorStyles", labelKey: "resources.tab.doorStyles" }, { id: "windowStyles", labelKey: "resources.tab.windowStyles" }, { id: "stairStyles", labelKey: "resources.tab.stairStyles" }, @@ -2995,6 +3031,10 @@ export interface ResourceManagerHandlers { onPatchCeilingType: (id: string, patch: Partial) => void; onAddCeilingType: () => void; onDeleteCeilingType: (id: string) => void; + /** Dachtypen (Dachaufbauten): immutable Änderung/Anlage/Löschung. */ + onPatchRoofType: (id: string, patch: Partial) => void; + onAddRoofType: () => void; + onDeleteRoofType: (id: string) => void; /** Import: fügt fertige (id-lose) Linienstile/Schraffuren hinzu (.lin/.pat). */ onImportLineStyles: (styles: Omit[]) => void; onImportHatches: (hatches: Omit[]) => void; @@ -3219,6 +3259,14 @@ export function ResourceManager({ onDeleteCeilingType={handlers.onDeleteCeilingType} /> )} + {tab === "roofStyles" && ( + + )} {tab === "doorStyles" && (