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.
This commit is contained in:
2026-07-05 03:01:35 +02:00
parent 26fbd11b73
commit dbf78a9d75
6 changed files with 122 additions and 6 deletions
+13
View File
@@ -2337,6 +2337,16 @@ export default function App() {
onSetOpeningDir: (openingDir) => { onSetOpeningDir: (openingDir) => {
if (selection?.kind === "opening") updateOpening(selection.id, { 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) ──────────────────── // ── Treppen-Attribute (nur bei selektierter Treppe) ────────────────────
onSetStairShape: (shape) => { onSetStairShape: (shape) => {
if (selection?.kind === "stair") updateStair(selection.id, { shape }); if (selection?.kind === "stair") updateStair(selection.id, { shape });
@@ -2356,6 +2366,9 @@ export default function App() {
onSetStairUp: (up) => { onSetStairUp: (up) => {
if (selection?.kind === "stair") updateStair(selection.id, { 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) ───────────────────────── // ── Raum-Attribute (nur bei selektiertem Raum) ─────────────────────────
onSetRoomName: (name) => { onSetRoomName: (name) => {
if (selection?.kind === "room") updateRoom(selection.id, { name }); if (selection?.kind === "room") updateRoom(selection.id, { name });
+15
View File
@@ -301,6 +301,16 @@ export const de = {
"objinfo.opening.dir.out": "Außen", "objinfo.opening.dir.out": "Außen",
"objinfo.opening.bottom": "Unterkante (UK)", "objinfo.opening.bottom": "Unterkante (UK)",
"objinfo.opening.top": "Oberkante (OK)", "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) ─────────────────────────────── // ── Treppen-Attribute (Object-Info-Panel) ───────────────────────────────
"objinfo.stair.section": "Treppe", "objinfo.stair.section": "Treppe",
"objinfo.stair.shape": "Grundform", "objinfo.stair.shape": "Grundform",
@@ -318,6 +328,11 @@ export const de = {
"objinfo.stair.refFloor": "Referenzgeschoss", "objinfo.stair.refFloor": "Referenzgeschoss",
"objinfo.stair.bottom": "Unterkante (UK)", "objinfo.stair.bottom": "Unterkante (UK)",
"objinfo.stair.top": "Oberkante (OK)", "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) ────────────────────────────────── // ── Raum-Attribute (Object-Info-Panel) ──────────────────────────────────
"objinfo.room.section": "Raum", "objinfo.room.section": "Raum",
"objinfo.room.name": "Name", "objinfo.room.name": "Name",
+13
View File
@@ -301,6 +301,15 @@ export const en: Record<TranslationKey, string> = {
"objinfo.opening.dir.out": "Out", "objinfo.opening.dir.out": "Out",
"objinfo.opening.bottom": "Bottom (UK)", "objinfo.opening.bottom": "Bottom (UK)",
"objinfo.opening.top": "Top (OK)", "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.section": "Stair",
"objinfo.stair.shape": "Shape", "objinfo.stair.shape": "Shape",
"objinfo.stair.shape.straight": "Straight", "objinfo.stair.shape.straight": "Straight",
@@ -317,6 +326,10 @@ export const en: Record<TranslationKey, string> = {
"objinfo.stair.refFloor": "Reference floor", "objinfo.stair.refFloor": "Reference floor",
"objinfo.stair.bottom": "Bottom (UK)", "objinfo.stair.bottom": "Bottom (UK)",
"objinfo.stair.top": "Top (OK)", "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) ────────────────────────────────── // ── Room attributes (object info panel) ──────────────────────────────────
"objinfo.room.section": "Room", "objinfo.room.section": "Room",
"objinfo.room.name": "Name", "objinfo.room.name": "Name",
+61 -6
View File
@@ -297,6 +297,22 @@ function StairSection({
/> />
</div> </div>
{/* Referenzpunkt der Laufbreite (links/mitte/rechts). */}
<div className="objinfo-field">
<span className="objinfo-flabel">{t("objinfo.stair.referenz")}</span>
<Dropdown
value={stair.referenz ?? "mitte"}
onChange={(v) => 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}
/>
</div>
{/* Laufrichtung (aufwärts/abwärts). */} {/* Laufrichtung (aufwärts/abwärts). */}
<div className="objinfo-field"> <div className="objinfo-field">
<span className="objinfo-flabel">{t("objinfo.stair.direction")}</span> <span className="objinfo-flabel">{t("objinfo.stair.direction")}</span>
@@ -426,16 +442,55 @@ function OpeningSection({
onCommit={(v) => host.onSetOpeningHeight(v)} onCommit={(v) => host.onSetOpeningHeight(v)}
/> />
{!isDoor && ( {!isDoor && (
<DimensionField <>
label={t("objinfo.opening.sill")} <DimensionField
value={opening.sillHeight} label={t("objinfo.opening.sill")}
onCommit={(v) => host.onSetOpeningSill(v)} value={opening.sillHeight}
/> onCommit={(v) => host.onSetOpeningSill(v)}
/>
{/* Fenster-spezifisch: Flügelanzahl (14). */}
<DimensionField
label={t("objinfo.opening.wingCount")}
value={opening.wingCount ?? 1}
min={1}
onCommit={(v) => 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 && ( {isDoor && (
<> <>
{/* Tür-Typ (normal / Wandöffnung). */}
<div className="objinfo-field">
<span className="objinfo-flabel">{t("objinfo.opening.doorType")}</span>
<Dropdown
value={opening.doorType ?? "normal"}
onChange={(v) => 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}
/>
</div>
{/* Sturzlinien-Darstellung (keine/innen/aussen/beide). */}
<div className="objinfo-field">
<span className="objinfo-flabel">{t("objinfo.opening.lintelLines")}</span>
<Dropdown
value={opening.lintelLines ?? "beide"}
onChange={(v) => 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}
/>
</div>
<div className="objinfo-field"> <div className="objinfo-field">
<span className="objinfo-flabel">{t("objinfo.opening.hinge")}</span> <span className="objinfo-flabel">{t("objinfo.opening.hinge")}</span>
<Dropdown <Dropdown
+8
View File
@@ -236,6 +236,12 @@ export interface PanelHostValue {
onSetOpeningSwing: (swing: "left" | "right") => void; onSetOpeningSwing: (swing: "left" | "right") => void;
/** Setzt die Aufschlagrichtung der Tür (innen/außen). */ /** Setzt die Aufschlagrichtung der Tür (innen/außen). */
onSetOpeningDir: (dir: "in" | "out") => void; onSetOpeningDir: (dir: "in" | "out") => void;
/** Setzt die Flügelanzahl des Fensters (14). */
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) ─── // ── Treppen-Attribute (Object-Info-Panel; nur die selektierte Treppe) ───
/** Setzt die Grundform (gerade/L/Wendel). */ /** Setzt die Grundform (gerade/L/Wendel). */
@@ -248,6 +254,8 @@ export interface PanelHostValue {
onSetStairRise: (totalRise: number) => void; onSetStairRise: (totalRise: number) => void;
/** Setzt die Laufrichtung (aufwärts/abwärts). */ /** Setzt die Laufrichtung (aufwärts/abwärts). */
onSetStairUp: (up: boolean) => void; 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) ──────── // ── Raum-Attribute (Object-Info-Panel; nur der selektierte Raum) ────────
/** Setzt den Raum-Namen. */ /** Setzt den Raum-Namen. */
+12
View File
@@ -137,6 +137,12 @@ export interface OpeningInfo {
swing?: "left" | "right"; swing?: "left" | "right";
swingAngle?: number; swingAngle?: number;
openingDir?: "in" | "out"; openingDir?: "in" | "out";
/** Nur Fenster: Anzahl der Flügel (14). 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). */ /** Aufgelöste absolute UK/OK (Meter). */
zBottom: number; zBottom: number;
zTop: number; zTop: number;
@@ -160,6 +166,8 @@ export interface StairInfo {
treadDepth: number; treadDepth: number;
/** Laufrichtung aufwärts (Auf-/Abpfeil-Richtung). */ /** Laufrichtung aufwärts (Auf-/Abpfeil-Richtung). */
up: boolean; up: boolean;
/** Referenzpunkt der Laufbreite (links/mitte/rechts). Fehlt → "mitte". */
referenz?: "links" | "mitte" | "rechts";
/** Geschoss, dem die Treppe zugeordnet ist. */ /** Geschoss, dem die Treppe zugeordnet ist. */
floorId: string; floorId: string;
floorName: string; floorName: string;
@@ -478,6 +486,9 @@ function openingSelection(project: Project, o: Opening): Selection {
swing: o.swing, swing: o.swing,
swingAngle: o.swingAngle, swingAngle: o.swingAngle,
openingDir: o.openingDir, openingDir: o.openingDir,
wingCount: o.wingCount,
doorType: o.doorType,
lintelLines: o.lintelLines,
zBottom: ext.zBottom, zBottom: ext.zBottom,
zTop: ext.zTop, zTop: ext.zTop,
}; };
@@ -513,6 +524,7 @@ function stairSelection(project: Project, s: Stair): Selection {
riserHeight: geo.riserHeight, riserHeight: geo.riserHeight,
treadDepth: geo.treadDepth, treadDepth: geo.treadDepth,
up: s.up !== false, up: s.up !== false,
referenz: s.referenz,
floorId: s.floorId, floorId: s.floorId,
floorName: floor?.name ?? s.floorId, floorName: floor?.name ?? s.floorId,
zBottom, zBottom,