Korrektur: Wand/Decke bleiben bei "Nach Bauteil" als Default, nur
Drawing2D wechselt auf "Nach Ebene"
Nutzer-Korrektur zum vorigen Commit: "eine Wand usw soll weiterhin
nach Bauteil haben und die weisser Grund und schwarzer Vordergrund
haben. Also nach Bauteil. 2D Elemente haben aber bei Attribute kein
nach Bauteil!!!" -- der vorige Commit hatte den Default global (auch
für Wand/Decke) auf "Nach Ebene" umgestellt, was die neutrale SIA-
Poché-Konvention (weisser Grund/schwarzer Vordergrund über die
Bauteil-Kette) durch die rohe Ebenenfarbe ersetzt hätte.
resolveForeground/resolveBackground/resolveHatchId/resolveStrokeWeight
(plan/generatePlan/shared.ts) sind zurückgesetzt auf ihr ursprüngliches
Verhalten: `source === "layer"` (fehlend/"object" ⇒ weiterhin Bauteil-
Kette, DEFAULT bei Wand/Decke). Der elementart-abhängige Default sitzt
jetzt an den AUFRUFERN statt im generischen Resolver:
• Wand/Decke (selectionInfo.ts): rohes Source-Feld unverändert
durchgereicht -- Default bleibt "Nach Bauteil".
• Drawing2D (selectionInfo.ts drawingSelection): `d.foregroundSource
?? "layer"` usw. VOR dem Resolver -- Default wird dort explizit
"Nach Ebene" (kein eigenes Bauteil, "Nach Bauteil" bietet das Panel
für 2D-Elemente ohnehin nicht mehr an, s. vorletzter Commit).
• AttributesPanel.tsx uiSourceOf() bekommt einen isDrawing-Parameter
für denselben elementart-abhängigen Default in der Dropdown-
Anzeige.
+Tests in shared.resolve.test.ts auf die jetzt korrekten Erwartungen
umgeschrieben (Default bleibt Bauteil, explizites "layer" liefert die
Kategorie, Drawing2D-Aufrufer-Mapping separat geprüft). tsc/vitest
934/934 grün.
This commit is contained in:
+11
-5
@@ -831,12 +831,18 @@ export interface LayerCategory {
|
||||
* Quelle eines vererbbaren Attributs (Vordergrund/Hintergrund/Strichstärke/
|
||||
* Schraffur), wenn KEIN expliziter Wert am Element gesetzt ist:
|
||||
* • "layer" — „Nach Ebene": die LayerCategory des Elements erzwingt den Wert
|
||||
* (`color`/`lw`/`hatch`). Das ist auch der DEFAULT, wenn das Source-Feld
|
||||
* fehlt (`undefined`) — ein frisch erzeugtes Element (Source-Feld nie
|
||||
* gesetzt) folgt also der Ebene, nicht dem Bauteil.
|
||||
* (`color`/`lw`/`hatch`).
|
||||
* • "object" — „Nach Bauteil": erbt vom Bauteil (Component) bzw. dessen
|
||||
* bisheriger Fallback-Kette. Muss aktiv im Attribut-Panel gewählt werden
|
||||
* (nur bei Wand/Decke anwendbar — ein Drawing2D hat kein Bauteil).
|
||||
* bisheriger Fallback-Kette (bei Wand/Decke u. a. die neutrale SIA-Poché-
|
||||
* Konvention, s. `resolveBackground`). Das ist auch der DEFAULT bei Wand/
|
||||
* Decke, wenn das Source-Feld fehlt (`undefined`) — bewusst NICHT „Nach
|
||||
* Ebene": eine frisch erzeugte Wand soll die neutrale Bauteil-Konvention
|
||||
* zeigen (weisser Grund, schwarzer Vordergrund), nicht die rohe
|
||||
* Ebenenfarbe.
|
||||
* Ein Drawing2D hat KEIN eigenes Bauteil — „Nach Bauteil" ist dort im
|
||||
* Attribut-Panel gar nicht erst wählbar, ein fehlendes Source-Feld wird dort
|
||||
* vom Aufrufer (`selectionInfo.ts`) auf „Nach Ebene" gemappt (die einzig
|
||||
* sinnvolle Alternative zum expliziten Wert bei einem bauteillosen Element).
|
||||
* Ein gesetzter expliziter Wert (z. B. `foreground`) gewinnt IMMER, unabhängig
|
||||
* von der Source (s. Resolve-Reihenfolge in `plan/generatePlan/shared.ts`).
|
||||
*/
|
||||
|
||||
@@ -50,11 +50,19 @@ import {
|
||||
* selbst gespeichert). */
|
||||
type UiSource = "layer" | "object" | "custom";
|
||||
|
||||
/** Leitet den UI-Quellen-Zustand ab: ein gesetzter Wert gewinnt immer („eigener
|
||||
* Wert"), sonst das rohe Source-Feld (`undefined` ⇒ „Nach Ebene", Default —
|
||||
* s. AttributeSource in model/types.ts). */
|
||||
function uiSourceOf(hasValue: boolean, raw: AttributeSource | undefined): UiSource {
|
||||
return hasValue ? "custom" : raw ?? "layer";
|
||||
/**
|
||||
* Leitet den UI-Quellen-Zustand ab: ein gesetzter Wert gewinnt immer („eigener
|
||||
* Wert"), sonst das rohe Source-Feld — dessen DEFAULT unterscheidet sich nach
|
||||
* Elementart (s. AttributeSource in model/types.ts): Wand/Decke „Nach Bauteil"
|
||||
* (neutrale SIA-Poché), Drawing2D „Nach Ebene" (kein eigenes Bauteil, „Nach
|
||||
* Bauteil" wird dort im Dropdown gar nicht erst angeboten).
|
||||
*/
|
||||
function uiSourceOf(
|
||||
hasValue: boolean,
|
||||
raw: AttributeSource | undefined,
|
||||
isDrawing: boolean,
|
||||
): UiSource {
|
||||
return hasValue ? "custom" : raw ?? (isDrawing ? "layer" : "object");
|
||||
}
|
||||
|
||||
export function AttributesPanel() {
|
||||
@@ -191,15 +199,21 @@ export function AttributesPanel() {
|
||||
</>
|
||||
);
|
||||
|
||||
// Vordergrund/Hintergrund: „eigener Wert" ⇒ auf Quelle "object" (Nach
|
||||
// Bauteil, Default) zurückfallen; „Nach Ebene"/„Nach Bauteil" schreiben die
|
||||
// Quelle direkt (der Host löscht dabei den expliziten Wert).
|
||||
const fgUi = uiSourceOf(sel.foreground !== undefined, sel.foregroundSource);
|
||||
const bgUi = uiSourceOf(sel.background !== undefined, sel.backgroundSource);
|
||||
const weightUi = uiSourceOf(sel.strokeWeightOverride !== undefined, sel.strokeWeightSource);
|
||||
// Vordergrund/Hintergrund: „eigener Wert" ⇒ auf den elementart-abhängigen
|
||||
// Default zurückfallen (Wand/Decke „Nach Bauteil", Drawing2D „Nach Ebene",
|
||||
// s. uiSourceOf); „Nach Ebene"/„Nach Bauteil" schreiben die Quelle direkt
|
||||
// (der Host löscht dabei den expliziten Wert).
|
||||
const fgUi = uiSourceOf(sel.foreground !== undefined, sel.foregroundSource, isDrawing);
|
||||
const bgUi = uiSourceOf(sel.background !== undefined, sel.backgroundSource, isDrawing);
|
||||
const weightUi = uiSourceOf(
|
||||
sel.strokeWeightOverride !== undefined,
|
||||
sel.strokeWeightSource,
|
||||
isDrawing,
|
||||
);
|
||||
const hatchUi = uiSourceOf(
|
||||
sel.fillHatchId !== undefined && sel.fillHatchId !== null,
|
||||
sel.hatchSource,
|
||||
isDrawing,
|
||||
);
|
||||
|
||||
// Wechsel auf „eigener Wert" per Dropdown (nicht durchs Editieren des Felds
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
/**
|
||||
* `resolveForeground`/`resolveBackground`/`resolveHatchId`/`resolveStrokeWeight`
|
||||
* — By-Layer/By-Object-Auflösung. Nutzer-Report: ein neu erzeugtes Element
|
||||
* (Source-Feld nie gesetzt) zeigte im Attribut-Panel „Nach Bauteil" als
|
||||
* Default, obwohl „Nach Ebene" der gewünschte Standard ist. Fix: `source`
|
||||
* fehlend/`"layer"` liefert jetzt die Kategorie, NUR ein explizites
|
||||
* `"object"` fällt auf das Bauteil zurück (vorher umgekehrt).
|
||||
* — By-Layer/By-Object-Auflösung. Der Resolver selbst bleibt „Nach Bauteil"
|
||||
* (Component) als Default, wenn `source` fehlt/`"object"` ist — das liefert
|
||||
* bei Wand/Decke die neutrale SIA-Poché-Konvention (weisser Grund, schwarzer
|
||||
* Vordergrund), NICHT die rohe Ebenenfarbe (Nutzer-Bestätigung: „eine Wand
|
||||
* usw soll weiterhin nach Bauteil haben"). Ein Drawing2D hat kein eigenes
|
||||
* Bauteil und bietet „Nach Bauteil" im Attribut-Panel gar nicht an — DORT
|
||||
* mappt der Aufrufer (`selectionInfo.ts`) ein fehlendes Source-Feld explizit
|
||||
* auf `"layer"`, bevor er diese Resolver aufruft (separat unten geprüft).
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
@@ -36,46 +39,52 @@ const category: LayerCategory = {
|
||||
hatch: "sia-wall",
|
||||
};
|
||||
|
||||
describe("resolveForeground/resolveBackground — Default fehlender Source ist jetzt 'Nach Ebene'", () => {
|
||||
it("kein Override, Source fehlt: liefert die Kategorie-Farbe (nicht mehr die Bauteilfarbe)", () => {
|
||||
expect(resolveForeground(comp, undefined, category, undefined)).toBe(category.color);
|
||||
expect(resolveBackground(comp, undefined, category, undefined)).toBe(category.color);
|
||||
describe("resolveForeground/resolveBackground — Default bleibt 'Nach Bauteil'", () => {
|
||||
it("kein Override, Source fehlt: liefert weiterhin die Bauteilfarbe (Default unverändert)", () => {
|
||||
expect(resolveForeground(comp, undefined, category, undefined)).toBe(comp.foreground);
|
||||
expect(resolveBackground(comp, undefined, category, undefined)).toBe(comp.background);
|
||||
});
|
||||
|
||||
it("kein Override, Source explizit 'layer': unverändert die Kategorie-Farbe", () => {
|
||||
expect(resolveForeground(comp, undefined, category, "layer")).toBe(category.color);
|
||||
expect(resolveBackground(comp, undefined, category, "layer")).toBe(category.color);
|
||||
});
|
||||
|
||||
it("kein Override, Source explizit 'object': fällt weiterhin auf die Bauteilfarbe zurück", () => {
|
||||
it("kein Override, Source explizit 'object': unverändert die Bauteilfarbe", () => {
|
||||
expect(resolveForeground(comp, undefined, category, "object")).toBe(comp.foreground);
|
||||
expect(resolveBackground(comp, undefined, category, "object")).toBe(comp.background);
|
||||
});
|
||||
|
||||
it("kein Override, Source explizit 'layer': liefert die Kategorie-Farbe", () => {
|
||||
expect(resolveForeground(comp, undefined, category, "layer")).toBe(category.color);
|
||||
expect(resolveBackground(comp, undefined, category, "layer")).toBe(category.color);
|
||||
});
|
||||
|
||||
it("ein gesetzter Override gewinnt immer, unabhängig von der Source", () => {
|
||||
expect(resolveForeground(comp, "#ff00ff", category, "object")).toBe("#ff00ff");
|
||||
expect(resolveForeground(comp, "#ff00ff", category, "layer")).toBe("#ff00ff");
|
||||
expect(resolveForeground(comp, "#ff00ff", category, undefined)).toBe("#ff00ff");
|
||||
});
|
||||
});
|
||||
|
||||
it("ohne Kategorie fällt 'Nach Ebene' auf die Bauteilfarbe zurück (kein Crash)", () => {
|
||||
expect(resolveForeground(comp, undefined, undefined, undefined)).toBe(comp.foreground);
|
||||
describe("resolveHatchId — Default bleibt 'Nach Bauteil'", () => {
|
||||
it("kein Override, Source fehlt: liefert den Fallback (Bauteil-Schraffur)", () => {
|
||||
expect(resolveHatchId(undefined, undefined, category, comp.hatchId)).toBe(comp.hatchId);
|
||||
});
|
||||
it("kein Override, Source explizit 'layer': liefert die Kategorie-Schraffur", () => {
|
||||
expect(resolveHatchId(undefined, "layer", category, comp.hatchId)).toBe(category.hatch);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveHatchId — Default fehlender Source ist jetzt 'Nach Ebene'", () => {
|
||||
it("kein Override, Source fehlt: liefert die Kategorie-Schraffur", () => {
|
||||
expect(resolveHatchId(undefined, undefined, category, comp.hatchId)).toBe(category.hatch);
|
||||
describe("resolveStrokeWeight — Default bleibt 'Nach Bauteil'", () => {
|
||||
it("kein Override, Source fehlt: liefert den Fallback", () => {
|
||||
expect(resolveStrokeWeight(undefined, undefined, category, 0.13)).toBe(0.13);
|
||||
});
|
||||
it("kein Override, Source explizit 'object': liefert den Fallback (Bauteil-Schraffur)", () => {
|
||||
expect(resolveHatchId(undefined, "object", category, comp.hatchId)).toBe(comp.hatchId);
|
||||
it("kein Override, Source explizit 'layer': liefert die Kategorie-Strichstärke", () => {
|
||||
expect(resolveStrokeWeight(undefined, "layer", category, 0.13)).toBe(category.lw);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveStrokeWeight — Default fehlender Source ist jetzt 'Nach Ebene'", () => {
|
||||
it("kein Override, Source fehlt: liefert die Kategorie-Strichstärke", () => {
|
||||
expect(resolveStrokeWeight(undefined, undefined, category, 0.13)).toBe(category.lw);
|
||||
});
|
||||
it("kein Override, Source explizit 'object': liefert den Fallback", () => {
|
||||
expect(resolveStrokeWeight(undefined, "object", category, 0.13)).toBe(0.13);
|
||||
describe("Drawing2D-Panel-Vorschau (selectionInfo.ts) — mappt fehlende Source explizit auf 'layer'", () => {
|
||||
it("simuliert das Aufrufer-Mapping `d.foregroundSource ?? \"layer\"`: liefert die Kategorie-Farbe für ein Drawing2D ohne Bauteil", () => {
|
||||
const noComponent: Component = { id: "", name: "", color: "#000", hatchId: "", joinPriority: 0 };
|
||||
const drawingSource: "layer" | undefined = undefined; // wie ein frisches Drawing2D
|
||||
expect(resolveForeground(noComponent, undefined, category, drawingSource ?? "layer")).toBe(
|
||||
category.color,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -419,12 +419,15 @@ export function resolveHatch(
|
||||
* Kollabiert die Vordergrund-Farbkette (Muster-/Schraffurlinienfarbe) eines
|
||||
* Bauteils zu einem einzelnen Wert für {@link resolveHatch}. AUFLÖSUNGSREIHENFOLGE
|
||||
* (By-Layer/By-Object, siehe `docs`/Attribut-Panel):
|
||||
* (Attribut-Override `override`) ?? (`source!=="object"` ⇒ `category.color`) ??
|
||||
* (Attribut-Override `override`) ?? (`source==="layer"` ⇒ `category.color`) ??
|
||||
* (`Component.foreground`) ?? `undefined`.
|
||||
* `source` fehlt/`"layer"` ⇒ „Nach Ebene" (DEFAULT — ein neu erzeugtes Element
|
||||
* ohne explizites Source-Feld folgt der Ebene, nicht dem Bauteil; „Nach
|
||||
* Bauteil" muss aktiv gewählt werden). Nur ein EXPLIZITES `"object"` fällt auf
|
||||
* die Bauteil-Kette zurück.
|
||||
* `source` fehlt/`"object"` ⇒ „Nach Bauteil" — bei WAND/DECKE bewusst der
|
||||
* DEFAULT (die Bauteil-Kette liefert die neutrale SIA-Poché, s.
|
||||
* {@link resolveBackground}). Ein Drawing2D hat KEIN eigenes Bauteil und
|
||||
* bietet „Nach Bauteil" im Attribut-Panel gar nicht erst an — dort wird ein
|
||||
* fehlendes Source-Feld vom AUFRUFER (`selectionInfo.ts`) explizit auf
|
||||
* `"layer"` gemappt, bevor er hier ankommt (dieser Resolver selbst bleibt
|
||||
* Bauteil-Default, unabhängig von der Elementart).
|
||||
*/
|
||||
export function resolveForeground(
|
||||
comp: Component,
|
||||
@@ -433,20 +436,22 @@ export function resolveForeground(
|
||||
source?: AttributeSource,
|
||||
): string | undefined {
|
||||
if (override != null) return override;
|
||||
if (source !== "object") return category?.color ?? comp.foreground;
|
||||
if (source === "layer") return category?.color ?? comp.foreground;
|
||||
return comp.foreground;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kollabiert die Hintergrund-/Füllfarbkette (Poché-Override) eines Bauteils zu
|
||||
* einem einzelnen Wert, exakt analog zu {@link resolveForeground}:
|
||||
* (Attribut-Override `override`) ?? (`source!=="object"` ⇒ `category.color`) ??
|
||||
* (Attribut-Override `override`) ?? (`source==="layer"` ⇒ `category.color`) ??
|
||||
* (`Component.background`) ?? `undefined`.
|
||||
* `undefined` als Endergebnis heißt „kein Override gesetzt" — Aufrufer fallen
|
||||
* dann auf ihren eigenen Default zurück (bei Wand/Decke die neutrale SIA-
|
||||
* Poché-Farbe HATCH_INK/HATCH_PAPER, NICHT `Component.color` — die reale
|
||||
* Bauteilfarbe soll die Schnitt-Konvention nicht überschreiben, nur ein
|
||||
* bewusster Override).
|
||||
* bewusster Override). Genau DIESE neutrale Poché ist bei Wand/Decke der
|
||||
* gewünschte Default („weisser Grund, schwarzer Vordergrund") — deshalb
|
||||
* bleibt „Nach Bauteil" dort der Default, nicht „Nach Ebene".
|
||||
*/
|
||||
export function resolveBackground(
|
||||
comp: Component,
|
||||
@@ -455,7 +460,7 @@ export function resolveBackground(
|
||||
source?: AttributeSource,
|
||||
): string | undefined {
|
||||
if (override != null) return override;
|
||||
if (source !== "object") return category?.color ?? comp.background;
|
||||
if (source === "layer") return category?.color ?? comp.background;
|
||||
return comp.background;
|
||||
}
|
||||
|
||||
@@ -463,10 +468,11 @@ export function resolveBackground(
|
||||
* Kollabiert die Schraffur-Referenz (Hatch Manager) eines Elements zu einer
|
||||
* einzelnen `hatchId` für {@link resolveHatch} — By-Layer/By-Object, analog zu
|
||||
* {@link resolveForeground}:
|
||||
* (Attribut-Override `override`) ?? (`source!=="object"` ⇒ `category.hatch`) ??
|
||||
* (Attribut-Override `override`) ?? (`source==="layer"` ⇒ `category.hatch`) ??
|
||||
* `fallback` (Bauteil-Schraffur, z. B. `Component.hatchId`/`viewHatchId`, oder
|
||||
* `undefined` bei Drawing2D ohne Bauteil-Bezug).
|
||||
* `source` fehlt/`"layer"` ⇒ „Nach Ebene" (DEFAULT, s. {@link resolveForeground}).
|
||||
* `source` fehlt/`"object"` ⇒ „Nach Bauteil" (DEFAULT bei Wand/Decke, s.
|
||||
* {@link resolveForeground}).
|
||||
* Ergebnis kann `undefined` sein — der Aufrufer fällt dann auf `NO_HATCH` zurück.
|
||||
*/
|
||||
export function resolveHatchId(
|
||||
@@ -476,17 +482,18 @@ export function resolveHatchId(
|
||||
fallback?: string,
|
||||
): string | undefined {
|
||||
if (override != null) return override;
|
||||
if (source !== "object") return category?.hatch ?? fallback;
|
||||
if (source === "layer") return category?.hatch ?? fallback;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kollabiert die Strichstärke-Kette (mm Papier) eines Elements, By-Layer/By-
|
||||
* Object, analog zu {@link resolveHatchId}:
|
||||
* (Attribut-Override `override`) ?? (`source!=="object"` ⇒ `category.lw`) ??
|
||||
* (Attribut-Override `override`) ?? (`source==="layer"` ⇒ `category.lw`) ??
|
||||
* `fallback` (bisherige Bauteil-/LineStyle-/Kategorie-Default-Kette des
|
||||
* Aufrufers — heutiges Verhalten).
|
||||
* `source` fehlt/`"layer"` ⇒ „Nach Ebene" (DEFAULT, s. {@link resolveForeground}).
|
||||
* `source` fehlt/`"object"` ⇒ „Nach Bauteil" (DEFAULT bei Wand/Decke, s.
|
||||
* {@link resolveForeground}).
|
||||
*/
|
||||
export function resolveStrokeWeight(
|
||||
override: number | undefined,
|
||||
@@ -495,7 +502,7 @@ export function resolveStrokeWeight(
|
||||
fallback: number,
|
||||
): number {
|
||||
if (override != null) return override;
|
||||
if (source !== "object") return category?.lw ?? fallback;
|
||||
if (source === "layer") return category?.lw ?? fallback;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
|
||||
@@ -1067,16 +1067,19 @@ function drawingSelection(project: Project, d: Drawing2D): Selection {
|
||||
// „Nach Bauteil"-Option im Panel, s. AttributesPanel.tsx) — als Platzhalter
|
||||
// ein leeres Bauteil, dessen foreground/background/hatchId immer fehlen,
|
||||
// sodass die Kette bei einem (aus Alt-Daten) expliziten `source:"object"`
|
||||
// korrekt auf „kein Wert" statt einer erfundenen Bauteilfarbe fällt.
|
||||
// korrekt auf „kein Wert" statt einer erfundenen Bauteilfarbe fällt. Anders
|
||||
// als bei Wand/Decke (Default „Nach Bauteil") gilt bei Drawing2D „Nach
|
||||
// Ebene" als Default — daher hier explizit `?? "layer"` VOR dem Resolver
|
||||
// (der selbst weiterhin generisch „Nach Bauteil" als Default kennt).
|
||||
const noComponent = { id: "", name: "", color, hatchId: "", joinPriority: 0 };
|
||||
const effectiveForeground = closed
|
||||
? resolveForeground(noComponent, d.foreground, cat, d.foregroundSource)
|
||||
? resolveForeground(noComponent, d.foreground, cat, d.foregroundSource ?? "layer")
|
||||
: undefined;
|
||||
const effectiveBackground = closed
|
||||
? resolveBackground(noComponent, d.background, cat, d.backgroundSource)
|
||||
? resolveBackground(noComponent, d.background, cat, d.backgroundSource ?? "layer")
|
||||
: undefined;
|
||||
const effectiveHatchId = closed
|
||||
? resolveHatchId(d.hatchId, d.hatchSource, cat, undefined)
|
||||
? resolveHatchId(d.hatchId, d.hatchSource ?? "layer", cat, undefined)
|
||||
: null;
|
||||
return {
|
||||
kind: "drawing2d",
|
||||
|
||||
Reference in New Issue
Block a user