Attribut-Panel: Default "Nach Ebene" statt "Nach Bauteil", Wert immer
sichtbar, Bearbeiten schaltet automatisch auf "eigener Wert" Nutzer-Report: "aktuell zeigt jedes Element standard nach Bauteil. Das wäre eigentlich 'custom' also eigener Wert. Deshalb es soll nach Ebene Standard sein bei allen Dingen. Und man sollte immer sehen welche Stiftdicke oder welche Farbe... und wenn man auf das Farbfeld klickt und die Farbe ändert dann springt es auf eigener Wert automatisch." **Default-Umkehr** (plan/generatePlan/shared.ts): resolveForeground/ resolveBackground/resolveHatchId/resolveStrokeWeight prüften bisher `source === "layer"`, sonst (auch bei fehlendem Source-Feld -- der Normalfall bei jedem neu erzeugten Element, das nie explizit gesetzt wird) fiel die Kette auf "Nach Bauteil" zurück. Jetzt `source !== "object"`: fehlend/"layer" liefert die Kategorie, NUR ein explizites "object" fällt noch auf das Bauteil zurück. Zentraler Fix in den Resolver-Funktionen selbst wirkt automatisch überall (Grundriss, Schnitt, Attribut-Panel-Vorschau) konsistent, nicht nur im Panel. Rückfrage an den Nutzer zum riskantesten Teil (Hintergrund/Poché hat eine dokumentierte SIA-neutrale Sonderregel, falls kein Wert gesetzt ist) -- bestätigt: einheitlich umstellen, "Nach Bauteil" bleibt bei Wand/Decke als explizite Wahl verfügbar. **"Nach Bauteil" nur noch bei Wand/Decke** (AttributesPanel.tsx): ein Drawing2D hat kein eigenes Bauteil (Component) -- die Option wäre dort bedeutungslos (resolveForeground & Co. fielen auf gar keinen Wert zurück). Neuer `allowObjectSource`-Schalter blendet die Dropdown- Option für alle vier Felder bei 2D-Elementen aus. **Wert immer sichtbar + Auto-Switch auf "eigener Wert"**: das Eingabe- Element (Farb-Swatch/Zahlenfeld/Schraffur-Dropdown) war bisher nur bei Quelle "eigener Wert" sichtbar -- jetzt immer, mit dem EFFEKTIVEN Wert befüllt. Neue Selection-Felder effectiveForeground/effectiveBackground/ effectiveHatchId (selectionInfo.ts, über dieselbe Resolve-Kette wie der Renderer) liefern dafür den echten Wert statt des oft leeren rohen Overrides. Editiert man das Feld direkt, greift der bestehende Override- Setter (setzt z.B. wall.foreground) -- die Quelle springt automatisch auf "eigener Wert", weil uiSourceOf ausschliesslich davon abhängt, ob ein Wert gesetzt ist (kein zusätzlicher Umschalt-Schritt nötig). Beim expliziten Umschalten per Dropdown wird jetzt vom aktuell ANGEZEIGTEN effektiven Wert gesät statt von sel.color, damit die Farbe dabei nicht unerwartet springt. +9 Tests (shared.resolve.test.ts: Default-Umkehr aller vier Resolver, Override gewinnt immer, Fallback ohne Kategorie). tsc/vitest 934/934 grün.
This commit is contained in:
@@ -51,9 +51,10 @@ import {
|
||||
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 Bauteil", Default). */
|
||||
* 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 ?? "object";
|
||||
return hasValue ? "custom" : raw ?? "layer";
|
||||
}
|
||||
|
||||
export function AttributesPanel() {
|
||||
@@ -131,13 +132,21 @@ export function AttributesPanel() {
|
||||
sel.kind === "ceiling" ||
|
||||
(isDrawing && sel.closed === true);
|
||||
|
||||
// Der 3-Optionen-Quellen-Dropdown, gemeinsam für alle vier Felder. Deaktiviert,
|
||||
// wenn das Feld für diese Elementart gar nicht gilt (z. B. Strichstärke bei
|
||||
// einer Treppe).
|
||||
// Ein Drawing2D hat KEIN eigenes Bauteil (Component) — „Nach Bauteil" wäre
|
||||
// dort bedeutungslos (resolveForeground & Co. fallen dann auf gar keinen
|
||||
// Wert zurück, s. selectionInfo.ts `noComponent`). Nur Wand/Decke bieten
|
||||
// die Option daher an.
|
||||
const allowObjectSource = !isDrawing;
|
||||
|
||||
// Der Quellen-Dropdown (Nach Ebene/Nach Bauteil/eigener Wert), gemeinsam für
|
||||
// alle vier Felder. Deaktiviert, wenn das Feld für diese Elementart gar
|
||||
// nicht gilt (z. B. Strichstärke bei einer Treppe); „Nach Bauteil" fehlt bei
|
||||
// Drawing2D (s. o.).
|
||||
const sourceSelect = (
|
||||
editable: boolean,
|
||||
ui: UiSource,
|
||||
onChange: (next: UiSource) => void,
|
||||
allowObject: boolean,
|
||||
) => (
|
||||
<Dropdown
|
||||
value={ui}
|
||||
@@ -145,33 +154,39 @@ export function AttributesPanel() {
|
||||
onChange={(v) => onChange(v as UiSource)}
|
||||
options={[
|
||||
{ value: "layer", label: t("attr.source.layer") },
|
||||
{ value: "object", label: t("attr.source.object") },
|
||||
...(allowObject ? [{ value: "object", label: t("attr.source.object") }] : []),
|
||||
{ value: "custom", label: t("attr.source.custom") },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
// Farb-Swatch-Eingabe für „eigener Wert" (Vordergrund/Hintergrund). Erscheint
|
||||
// nur, wenn die Quelle „custom" ist — sonst zeigt allein der Dropdown den
|
||||
// Zustand.
|
||||
const colorValue = (value: string | undefined, onSet: (color: string) => void) => (
|
||||
<ColorHexField value={value ?? "#808080"} onChange={onSet} />
|
||||
);
|
||||
// Farb-Swatch — IMMER sichtbar (Nutzer-Wunsch: den aktuell EFFEKTIVEN Wert
|
||||
// (welche Farbe auch immer Ebene/Bauteil/Override gerade liefert) jederzeit
|
||||
// sehen, nicht nur bei „eigener Wert"). Ändert man ihn direkt, wird
|
||||
// automatisch „eigener Wert": der Setter schreibt den Override, wodurch
|
||||
// `uiSourceOf` beim nächsten Render „custom" ableitet (kein separater
|
||||
// Umschalt-Schritt nötig).
|
||||
const colorValue = (
|
||||
value: string | undefined,
|
||||
onSet: (color: string) => void,
|
||||
editable: boolean,
|
||||
) => <ColorHexField value={value ?? "#808080"} onChange={onSet} disabled={!editable} />;
|
||||
|
||||
// Eine Attribut-Zeile: Label + Quellen-Dropdown + (nur bei „eigener Wert")
|
||||
// das passende Eingabe-Element.
|
||||
// Eine Attribut-Zeile: Label + IMMER sichtbares Eingabe-Element (zeigt den
|
||||
// effektiven Wert) + Quellen-Dropdown.
|
||||
const sourceRow = (
|
||||
labelKey: string,
|
||||
editable: boolean,
|
||||
ui: UiSource,
|
||||
onSource: (next: UiSource) => void,
|
||||
valueEditor: ReactNode,
|
||||
allowObject: boolean = allowObjectSource,
|
||||
) => (
|
||||
<>
|
||||
<span className="attr-key">{t(labelKey)}</span>
|
||||
<span className="attr-val">
|
||||
{ui === "custom" && valueEditor}
|
||||
{sourceSelect(editable, ui, onSource)}
|
||||
{valueEditor}
|
||||
{sourceSelect(editable, ui, onSource, allowObject)}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
@@ -187,12 +202,17 @@ export function AttributesPanel() {
|
||||
sel.hatchSource,
|
||||
);
|
||||
|
||||
// Wechsel auf „eigener Wert" per Dropdown (nicht durchs Editieren des Felds
|
||||
// selbst, s. colorValue-Kommentar oben) sät den Override mit dem gerade
|
||||
// ANGEZEIGTEN effektiven Wert — sonst würde die Farbe beim Umschalten
|
||||
// unerwartet springen (z. B. auf sel.color statt der tatsächlich sichtbaren
|
||||
// Ebenen-/Bauteilfarbe).
|
||||
const onForegroundSourceChange = (next: UiSource) => {
|
||||
if (next === "custom") host.onSetSelectionForeground(sel.foreground ?? sel.color);
|
||||
if (next === "custom") host.onSetSelectionForeground(sel.effectiveForeground ?? sel.color);
|
||||
else host.onSetSelectionForegroundSource(next);
|
||||
};
|
||||
const onBackgroundSourceChange = (next: UiSource) => {
|
||||
if (next === "custom") host.onSetSelectionBackground(sel.background ?? sel.color);
|
||||
if (next === "custom") host.onSetSelectionBackground(sel.effectiveBackground ?? sel.color);
|
||||
else host.onSetSelectionBackgroundSource(next);
|
||||
};
|
||||
const onWeightSourceChange = (next: UiSource) => {
|
||||
@@ -200,8 +220,11 @@ export function AttributesPanel() {
|
||||
else host.onSetSelectionStrokeWeightSource(next);
|
||||
};
|
||||
const onHatchSourceChange = (next: UiSource) => {
|
||||
if (next === "custom") host.onSetSelectionFill(project.hatches[0]?.id ?? null);
|
||||
else host.onSetSelectionHatchSource(next);
|
||||
if (next === "custom") {
|
||||
host.onSetSelectionFill(sel.effectiveHatchId ?? project.hatches[0]?.id ?? null);
|
||||
} else {
|
||||
host.onSetSelectionHatchSource(next);
|
||||
}
|
||||
};
|
||||
|
||||
// Effektiver Linienstil rein informativ (kein Setter im Kontrakt → kein Fake).
|
||||
@@ -272,6 +295,7 @@ export function AttributesPanel() {
|
||||
type="number"
|
||||
step={0.01}
|
||||
min={0}
|
||||
disabled={!weightEditable}
|
||||
list="attr-pen-weights"
|
||||
value={sel.weightMm}
|
||||
onChange={(e) => {
|
||||
@@ -352,7 +376,7 @@ export function AttributesPanel() {
|
||||
pocheEditable,
|
||||
fgUi,
|
||||
onForegroundSourceChange,
|
||||
colorValue(sel.foreground, host.onSetSelectionForeground),
|
||||
colorValue(sel.effectiveForeground, host.onSetSelectionForeground, pocheEditable),
|
||||
)}
|
||||
|
||||
{sourceRow(
|
||||
@@ -360,7 +384,7 @@ export function AttributesPanel() {
|
||||
pocheEditable,
|
||||
bgUi,
|
||||
onBackgroundSourceChange,
|
||||
colorValue(sel.background, host.onSetSelectionBackground),
|
||||
colorValue(sel.effectiveBackground, host.onSetSelectionBackground, pocheEditable),
|
||||
)}
|
||||
|
||||
{sourceRow(
|
||||
@@ -369,7 +393,8 @@ export function AttributesPanel() {
|
||||
hatchUi,
|
||||
onHatchSourceChange,
|
||||
<Dropdown
|
||||
value={sel.fillHatchId ?? ""}
|
||||
value={sel.effectiveHatchId ?? ""}
|
||||
disabled={!fillEditable}
|
||||
onChange={(v) => host.onSetSelectionFill(v || null)}
|
||||
options={[
|
||||
{ value: "", label: t("attr.none") },
|
||||
|
||||
Reference in New Issue
Block a user