Wandtyp-Kürzel: Component.abbrev + wallTypeLabel() + Kürzel-Feld im ResourceManager; Geschoss-Dropdown schmal

This commit is contained in:
2026-07-05 23:16:38 +02:00
parent 9133c0961d
commit 889cbb2c12
8 changed files with 65 additions and 11 deletions
+3
View File
@@ -545,6 +545,9 @@ export const de = {
"resources.components.placeholder": "Bauteil",
"resources.col.name": "Name",
"resources.col.color": "Farbe",
"resources.col.abbrev": "Kürzel",
"resources.col.abbrev.hint": "Kurz-Code für Wandtyp-Dropdowns (z. B. «BET», «HLZ»)",
"resources.col.abbrev.placeholder": "z. B. BET",
"resources.col.hatch": "Schnittschraffur",
"resources.col.viewHatch": "Ansichtsschraffur",
"resources.viewHatch.none": "(keine)",
+3
View File
@@ -539,6 +539,9 @@ export const en: Record<TranslationKey, string> = {
"resources.components.placeholder": "Component",
"resources.col.name": "Name",
"resources.col.color": "Color",
"resources.col.abbrev": "Code",
"resources.col.abbrev.hint": "Short code for wall-type dropdowns (e.g. «CON», «BRK»)",
"resources.col.abbrev.placeholder": "e.g. CON",
"resources.col.hatch": "Section hatch",
"resources.col.viewHatch": "View hatch",
"resources.viewHatch.none": "(none)",
+35
View File
@@ -227,6 +227,12 @@ export interface Component {
* Render-Modus „textured".
*/
material?: ComponentMaterial;
/**
* Optionales Kürzel für Wandtyp-Labels (z. B. „BET", „HLZ", „GKB", „DAE").
* Wird von `wallTypeLabel()` genutzt, um Wandtyp-Dropdowns kompakt darzustellen
* (z. B. „BET 24" statt dem vollen Namen). Fehlt es, greift der Name.
*/
abbrev?: string;
/** Verschneidungs-Rang: höher läuft am Stoß durch (Backbone). */
joinPriority: number;
}
@@ -1374,6 +1380,35 @@ export const collectVisibleCodes = (layers: LayerCategory[]): Set<string> => {
export const wallTypeThickness = (wt: WallType): number =>
wt.layers.reduce((sum, l) => sum + l.thickness, 0);
/**
* Kompakter Anzeigetext für einen Wandtyp im Dropdown (VW-Stil).
* - Einschichtig mit Kürzel: „BET 24" (Kürzel + Dicke in cm)
* - Mehrschichtig mit Kürzeln: „GKB·BET·DAE" + Gesamtdicke „(25.5)"
* - Ohne Kürzel: Name bleibt — kein Rückfall auf rohen Namen, nur keine Kurzform.
*
* `project.components` wird genutzt, um `Component.abbrev` aufzulösen.
* Ist kein abbrev gesetzt, erscheint der volle Name unverändert.
*/
export function wallTypeLabel(
wt: WallType | CeilingType,
components: Component[],
): string {
const resolve = (id: string) => components.find((c) => c.id === id);
const totalCm = +(wallTypeThickness(wt) * 100).toFixed(1);
// cm-Zahl: "24" statt "24.0", "17.5" bleibt "17.5"
const cmStr = totalCm % 1 === 0 ? String(totalCm | 0) : String(totalCm);
// Alle Schichten haben ein Kürzel → Kurzform bauen
const abbrevs = wt.layers.map((l) => resolve(l.componentId)?.abbrev ?? "");
const allHaveAbbrev = abbrevs.every((a) => a.length > 0);
if (allHaveAbbrev) {
if (wt.layers.length === 1) return `${abbrevs[0]} ${cmStr}`;
return `${abbrevs.join("·")} (${cmStr})`;
}
// Fallback: voller Name
return wt.name;
}
/** Formatiert Meter mit zwei Nachkommastellen, z. B. "0.35 m". */
export const formatM = (meters: number): string => meters.toFixed(2) + " m";
+6 -2
View File
@@ -24,7 +24,7 @@
import type { ReactNode } from "react";
import { t } from "../i18n";
import { formatM } from "../model/types";
import { formatM, wallTypeLabel } from "../model/types";
import { PEN_WEIGHTS } from "../model/types";
import type { AttributeSource } from "../model/types";
import { usePanelHost } from "./host";
@@ -75,7 +75,11 @@ export function AttributesPanel() {
value={host.activeWallTypeId}
disabled={!host.toolsEnabled}
onChange={(v) => host.onActiveWallTypeId(v)}
options={project.wallTypes.map((wt) => ({ value: wt.id, label: wt.name }))}
options={project.wallTypes.map((wt) => ({
value: wt.id,
label: wallTypeLabel(wt, project.components),
title: wt.name,
}))}
/>
</span>
</div>
+4 -8
View File
@@ -581,10 +581,8 @@ export function CeilingSection({
onChange={(id) => host.onSetCeilingType(id)}
options={ceiling.ceilingTypes.map((ct) => ({
value: ct.id,
label: t("objinfo.wall.presetLabel", {
name: ct.name,
thickness: ct.thickness.toFixed(2),
}),
label: ct.label,
title: ct.name,
}))}
title={t("objinfo.ceiling.preset")}
width={150}
@@ -717,10 +715,8 @@ export function WallSection({
onChange={(id) => host.onSetWallType(id)}
options={wall.wallTypes.map((wt) => ({
value: wt.id,
label: t("objinfo.wall.presetLabel", {
name: wt.name,
thickness: wt.thickness.toFixed(2),
}),
label: wt.label,
title: wt.name,
}))}
title={t("objinfo.wall.preset")}
width={150}
+5
View File
@@ -15,6 +15,7 @@ import {
flattenCategories,
getCeilingType,
getWallType,
wallTypeLabel,
wallTypeThickness,
} from "../model/types";
import type {
@@ -48,6 +49,8 @@ import { stairGeometry, stairBBox } from "../geometry/stair";
export interface WallTypeChoice {
id: string;
name: string;
/** Kurz-Label für das Dropdown (aus wallTypeLabel: Kürzel+Dicke oder Name). */
label: string;
/** Gesamtdicke (Meter) — informativ im Dropdown-Label. */
thickness: number;
/** Anzahl Schichten (1 = einschichtig). */
@@ -359,6 +362,7 @@ function wallSelection(project: Project, wall: Wall): Selection {
wallTypes: project.wallTypes.map((t) => ({
id: t.id,
name: t.name,
label: wallTypeLabel(t, project.components),
thickness: wallTypeThickness(t),
layerCount: t.layers.length,
})),
@@ -414,6 +418,7 @@ function ceilingSelection(project: Project, ceiling: Ceiling): Selection {
ceilingTypes: (project.ceilingTypes ?? []).map((t) => ({
id: t.id,
name: t.name,
label: wallTypeLabel(t, project.components),
thickness: wallTypeThickness(t),
layerCount: t.layers.length,
})),
+2 -1
View File
@@ -4040,7 +4040,8 @@ body {
padding-left: 56px;
}
.objinfo-subfield .tb-dd-trigger {
width: 100%;
width: auto;
max-width: 140px;
}
/* ── Attribute-Palette (AttributesPanel) ─────────────────────────────── */
+7
View File
@@ -763,6 +763,13 @@ function ComponentDetail({
</div>
<div className="res-fields">
<FieldRow label={t("resources.col.abbrev")} hint={t("resources.col.abbrev.hint")}>
<TextField
value={component.abbrev ?? ""}
onChange={(v) => onPatch({ abbrev: v || undefined })}
placeholder={t("resources.col.abbrev.placeholder")}
/>
</FieldRow>
<FieldRow
label={t("resources.col.foreground")}
hint={t("resources.col.foreground.hint")}