From dbf78a9d75f3dd2f2c20166f19bda94fc90f9111 Mon Sep 17 00:00:00 2001 From: Karim Date: Sun, 5 Jul 2026 03:01:35 +0200 Subject: [PATCH] UI: Gruppe-A-Felder in Objekt-Info-Panel verdrahtet - Treppe: Dropdown Referenz (Links/Mitte/Rechts) vor Laufrichtung. - Fenster: Feld Fluegel (1-4) nach Bruestung. - Tuer: Dropdowns Typ (Normal/Wandoeffnung) + Sturzlinien (Keine/Innen/Aussen/ Beide) vor Anschlag. - host.ts/selectionInfo.ts Durchreichung, App.tsx-Handler (kind-guard + updateOpening/updateStair), i18n de/en. Rueckwaertskompatibel. tsc sauber, vitest 292/292. --- src/App.tsx | 13 +++++++ src/i18n/de.ts | 15 ++++++++ src/i18n/en.ts | 13 +++++++ src/panels/ObjectInfoPanel.tsx | 67 +++++++++++++++++++++++++++++++--- src/panels/host.ts | 8 ++++ src/state/selectionInfo.ts | 12 ++++++ 6 files changed, 122 insertions(+), 6 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index f70077c..25eb859 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2337,6 +2337,16 @@ export default function App() { onSetOpeningDir: (openingDir) => { if (selection?.kind === "opening") updateOpening(selection.id, { openingDir }); }, + onSetOpeningWingCount: (wingCount) => { + if (selection?.kind === "opening") + updateOpening(selection.id, { wingCount: Math.max(1, Math.min(4, Math.round(wingCount))) }); + }, + onSetOpeningDoorType: (doorType) => { + if (selection?.kind === "opening") updateOpening(selection.id, { doorType }); + }, + onSetOpeningLintelLines: (lintelLines) => { + if (selection?.kind === "opening") updateOpening(selection.id, { lintelLines }); + }, // ── Treppen-Attribute (nur bei selektierter Treppe) ──────────────────── onSetStairShape: (shape) => { if (selection?.kind === "stair") updateStair(selection.id, { shape }); @@ -2356,6 +2366,9 @@ export default function App() { onSetStairUp: (up) => { if (selection?.kind === "stair") updateStair(selection.id, { up }); }, + onSetStairReferenz: (referenz) => { + if (selection?.kind === "stair") updateStair(selection.id, { referenz }); + }, // ── Raum-Attribute (nur bei selektiertem Raum) ───────────────────────── onSetRoomName: (name) => { if (selection?.kind === "room") updateRoom(selection.id, { name }); diff --git a/src/i18n/de.ts b/src/i18n/de.ts index 19b6459..6748c68 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -301,6 +301,16 @@ export const de = { "objinfo.opening.dir.out": "Außen", "objinfo.opening.bottom": "Unterkante (UK)", "objinfo.opening.top": "Oberkante (OK)", + // Neue Öffnungs-Felder + "objinfo.opening.wingCount": "Flügel", + "objinfo.opening.doorType": "Typ", + "objinfo.opening.doorType.normal": "Normal", + "objinfo.opening.doorType.wandoeffnung": "Wandöffnung", + "objinfo.opening.lintelLines": "Sturzlinien", + "objinfo.opening.lintelLines.keine": "Keine", + "objinfo.opening.lintelLines.innen": "Innen", + "objinfo.opening.lintelLines.aussen": "Außen", + "objinfo.opening.lintelLines.beide": "Beide", // ── Treppen-Attribute (Object-Info-Panel) ─────────────────────────────── "objinfo.stair.section": "Treppe", "objinfo.stair.shape": "Grundform", @@ -318,6 +328,11 @@ export const de = { "objinfo.stair.refFloor": "Referenzgeschoss", "objinfo.stair.bottom": "Unterkante (UK)", "objinfo.stair.top": "Oberkante (OK)", + // Neues Treppen-Feld + "objinfo.stair.referenz": "Referenz", + "objinfo.stair.referenz.links": "Links", + "objinfo.stair.referenz.mitte": "Mitte", + "objinfo.stair.referenz.rechts": "Rechts", // ── Raum-Attribute (Object-Info-Panel) ────────────────────────────────── "objinfo.room.section": "Raum", "objinfo.room.name": "Name", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 065fec8..9a5adc6 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -301,6 +301,15 @@ export const en: Record = { "objinfo.opening.dir.out": "Out", "objinfo.opening.bottom": "Bottom (UK)", "objinfo.opening.top": "Top (OK)", + "objinfo.opening.wingCount": "Wings", + "objinfo.opening.doorType": "Type", + "objinfo.opening.doorType.normal": "Normal", + "objinfo.opening.doorType.wandoeffnung": "Wall opening", + "objinfo.opening.lintelLines": "Lintel lines", + "objinfo.opening.lintelLines.keine": "None", + "objinfo.opening.lintelLines.innen": "Inside", + "objinfo.opening.lintelLines.aussen": "Outside", + "objinfo.opening.lintelLines.beide": "Both", "objinfo.stair.section": "Stair", "objinfo.stair.shape": "Shape", "objinfo.stair.shape.straight": "Straight", @@ -317,6 +326,10 @@ export const en: Record = { "objinfo.stair.refFloor": "Reference floor", "objinfo.stair.bottom": "Bottom (UK)", "objinfo.stair.top": "Top (OK)", + "objinfo.stair.referenz": "Reference", + "objinfo.stair.referenz.links": "Left", + "objinfo.stair.referenz.mitte": "Centre", + "objinfo.stair.referenz.rechts": "Right", // ── Room attributes (object info panel) ────────────────────────────────── "objinfo.room.section": "Room", "objinfo.room.name": "Name", diff --git a/src/panels/ObjectInfoPanel.tsx b/src/panels/ObjectInfoPanel.tsx index de46598..05a4a60 100644 --- a/src/panels/ObjectInfoPanel.tsx +++ b/src/panels/ObjectInfoPanel.tsx @@ -297,6 +297,22 @@ function StairSection({ /> + {/* Referenzpunkt der Laufbreite (links/mitte/rechts). */} +
+ {t("objinfo.stair.referenz")} + host.onSetStairReferenz(v as "links" | "mitte" | "rechts")} + options={[ + { value: "links", label: t("objinfo.stair.referenz.links") }, + { value: "mitte", label: t("objinfo.stair.referenz.mitte") }, + { value: "rechts", label: t("objinfo.stair.referenz.rechts") }, + ]} + title={t("objinfo.stair.referenz")} + width={130} + /> +
+ {/* Laufrichtung (aufwärts/abwärts). */}
{t("objinfo.stair.direction")} @@ -426,16 +442,55 @@ function OpeningSection({ onCommit={(v) => host.onSetOpeningHeight(v)} /> {!isDoor && ( - host.onSetOpeningSill(v)} - /> + <> + host.onSetOpeningSill(v)} + /> + {/* Fenster-spezifisch: Flügelanzahl (1–4). */} + host.onSetOpeningWingCount(Math.max(1, Math.min(4, Math.round(v))))} + /> + )} - {/* Tür-spezifisch: Anschlag / Aufschlagseite / Richtung / Winkel. */} + {/* Tür-spezifisch: Typ / Sturzlinien / Anschlag / Aufschlagseite / Richtung / Winkel. */} {isDoor && ( <> + {/* Tür-Typ (normal / Wandöffnung). */} +
+ {t("objinfo.opening.doorType")} + host.onSetOpeningDoorType(v as "normal" | "wandoeffnung")} + options={[ + { value: "normal", label: t("objinfo.opening.doorType.normal") }, + { value: "wandoeffnung", label: t("objinfo.opening.doorType.wandoeffnung") }, + ]} + title={t("objinfo.opening.doorType")} + width={130} + /> +
+ {/* Sturzlinien-Darstellung (keine/innen/aussen/beide). */} +
+ {t("objinfo.opening.lintelLines")} + host.onSetOpeningLintelLines(v as "keine" | "innen" | "aussen" | "beide")} + options={[ + { value: "keine", label: t("objinfo.opening.lintelLines.keine") }, + { value: "innen", label: t("objinfo.opening.lintelLines.innen") }, + { value: "aussen", label: t("objinfo.opening.lintelLines.aussen") }, + { value: "beide", label: t("objinfo.opening.lintelLines.beide") }, + ]} + title={t("objinfo.opening.lintelLines")} + width={130} + /> +
{t("objinfo.opening.hinge")} void; /** Setzt die Aufschlagrichtung der Tür (innen/außen). */ onSetOpeningDir: (dir: "in" | "out") => void; + /** Setzt die Flügelanzahl des Fensters (1–4). */ + onSetOpeningWingCount: (wingCount: number) => void; + /** Setzt den Tür-Typ (normal / Wandöffnung). */ + onSetOpeningDoorType: (doorType: "normal" | "wandoeffnung") => void; + /** Setzt die Sturzlinien-Darstellung der Tür. */ + onSetOpeningLintelLines: (lintelLines: "keine" | "innen" | "aussen" | "beide") => void; // ── Treppen-Attribute (Object-Info-Panel; nur die selektierte Treppe) ─── /** Setzt die Grundform (gerade/L/Wendel). */ @@ -248,6 +254,8 @@ export interface PanelHostValue { onSetStairRise: (totalRise: number) => void; /** Setzt die Laufrichtung (aufwärts/abwärts). */ onSetStairUp: (up: boolean) => void; + /** Setzt den Referenzpunkt der Laufbreite (links/mitte/rechts). */ + onSetStairReferenz: (referenz: "links" | "mitte" | "rechts") => void; // ── Raum-Attribute (Object-Info-Panel; nur der selektierte Raum) ──────── /** Setzt den Raum-Namen. */ diff --git a/src/state/selectionInfo.ts b/src/state/selectionInfo.ts index c7b090e..52ff0c7 100644 --- a/src/state/selectionInfo.ts +++ b/src/state/selectionInfo.ts @@ -137,6 +137,12 @@ export interface OpeningInfo { swing?: "left" | "right"; swingAngle?: number; openingDir?: "in" | "out"; + /** Nur Fenster: Anzahl der Flügel (1–4). Fehlt → 1 (Default). */ + wingCount?: number; + /** Nur Tür: Tür-Typ (normal / Wandöffnung). Fehlt → "normal". */ + doorType?: "normal" | "wandoeffnung"; + /** Nur Tür: Sturzlinien (keine/innen/aussen/beide). Fehlt → "beide". */ + lintelLines?: "keine" | "innen" | "aussen" | "beide"; /** Aufgelöste absolute UK/OK (Meter). */ zBottom: number; zTop: number; @@ -160,6 +166,8 @@ export interface StairInfo { treadDepth: number; /** Laufrichtung aufwärts (Auf-/Abpfeil-Richtung). */ up: boolean; + /** Referenzpunkt der Laufbreite (links/mitte/rechts). Fehlt → "mitte". */ + referenz?: "links" | "mitte" | "rechts"; /** Geschoss, dem die Treppe zugeordnet ist. */ floorId: string; floorName: string; @@ -478,6 +486,9 @@ function openingSelection(project: Project, o: Opening): Selection { swing: o.swing, swingAngle: o.swingAngle, openingDir: o.openingDir, + wingCount: o.wingCount, + doorType: o.doorType, + lintelLines: o.lintelLines, zBottom: ext.zBottom, zTop: ext.zTop, }; @@ -513,6 +524,7 @@ function stairSelection(project: Project, s: Stair): Selection { riserHeight: geo.riserHeight, treadDepth: geo.treadDepth, up: s.up !== false, + referenz: s.referenz, floorId: s.floorId, floorName: floor?.name ?? s.floorId, zBottom,