Raumstempel: Ausrichtung pro Zeile + Anker als Snappunkt
Jede Stempelzeile (Name, Name-2, Bodenflaeche, Nutzung) hat jetzt eine eigene Ausrichtung links/mitte/rechts, im Stempel-Editor pro Zeile waehlbar und in SVG- wie nativer Darstellung honoriert (text-anchor bzw. align). Bodenflaeche/Nutzung fallen auf mitte zurueck (bisheriges Bild). Der Stempel-Anker (stampAnchor bzw. Schwerpunkt) meldet sich zusaetzlich als Endpunkt-Snapkandidat an — gefiltert nach Geschoss und sichtbarer Kategorie; der ziehbare Griff war bereits verdrahtet.
This commit is contained in:
+117
-31
@@ -1,5 +1,6 @@
|
||||
// Schwebender Dialog zum Bearbeiten des strukturierten Raum-Stempels (Feldmodell):
|
||||
// Raumnummer, Raumname (+ Zeile 2), Bodenfläche (mit Präfix) und Nutzung. Ersetzt
|
||||
// Raumnummer, Raumname (+ Zeile 2), Bodenfläche (mit Präfix) und Nutzung. Jede
|
||||
// Zeile trägt zusätzlich eine eigene Ausrichtung (links/mitte/rechts). Ersetzt
|
||||
// für neue Räume den freien Text-Editor. Kontrolliert (value/onApply/onCancel);
|
||||
// arbeitet auf einem lokalen Entwurf, erst „Übernehmen" schreibt zurück.
|
||||
// Bezeichner englisch, UI-Text über t().
|
||||
@@ -10,6 +11,7 @@
|
||||
import { useState } from "react";
|
||||
import { t } from "../i18n";
|
||||
import type { RoomStamp } from "../model/types";
|
||||
import type { Align } from "../text/richText";
|
||||
|
||||
export interface RoomStampEditorProps {
|
||||
/** Ausgangsstempel (wird beim Öffnen kopiert; Editor arbeitet lokal). */
|
||||
@@ -20,11 +22,60 @@ export interface RoomStampEditorProps {
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
/** Die drei Ausrichtungen mit Icon und Beschriftung (für die Umschalter). */
|
||||
const ALIGN_CELLS: { align: Align; icon: string; titleKey: string }[] = [
|
||||
{ align: "left", icon: "format_align_left", titleKey: "text.align.left" },
|
||||
{ align: "center", icon: "format_align_center", titleKey: "text.align.center" },
|
||||
{ align: "right", icon: "format_align_right", titleKey: "text.align.right" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Drei-Wege-Umschalter für die Zeilen-Ausrichtung. `value` ist der aktuelle Wert
|
||||
* (fehlt er, gilt `fallback`); `onChange` liefert die gewählte Ausrichtung.
|
||||
*/
|
||||
function AlignToggle({
|
||||
value,
|
||||
fallback,
|
||||
onChange,
|
||||
disabled,
|
||||
label,
|
||||
}: {
|
||||
value: Align | undefined;
|
||||
fallback: Align;
|
||||
onChange: (align: Align) => void;
|
||||
disabled?: boolean;
|
||||
label: string;
|
||||
}) {
|
||||
const current = value ?? fallback;
|
||||
return (
|
||||
<div className="roomstamp-align" role="group" aria-label={label}>
|
||||
{ALIGN_CELLS.map((cell) => (
|
||||
<button
|
||||
key={cell.align}
|
||||
type="button"
|
||||
className={`roomstamp-align-btn${current === cell.align ? " active" : ""}`}
|
||||
disabled={disabled}
|
||||
aria-pressed={current === cell.align}
|
||||
title={t(cell.titleKey)}
|
||||
aria-label={t(cell.titleKey)}
|
||||
onClick={() => onChange(cell.align)}
|
||||
>
|
||||
<span className="material-symbols-outlined" aria-hidden="true">
|
||||
{cell.icon}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function RoomStampEditor({ value, onApply, onCancel }: RoomStampEditorProps) {
|
||||
const [draft, setDraft] = useState<RoomStamp>(value);
|
||||
|
||||
const patch = (p: Partial<RoomStamp>) => setDraft((d) => ({ ...d, ...p }));
|
||||
|
||||
const hasLine2 = (draft.nameLine2 ?? "").trim().length > 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="texteditor-overlay"
|
||||
@@ -66,34 +117,60 @@ export function RoomStampEditor({ value, onApply, onCancel }: RoomStampEditorPro
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="roomstamp-field">
|
||||
<span className="roomstamp-label">{t("roomStamp.name")}</span>
|
||||
<input
|
||||
type="text"
|
||||
className="roomstamp-input"
|
||||
value={draft.name}
|
||||
onChange={(e) => patch({ name: e.target.value })}
|
||||
<div className="roomstamp-row">
|
||||
<label className="roomstamp-field">
|
||||
<span className="roomstamp-label">{t("roomStamp.name")}</span>
|
||||
<input
|
||||
type="text"
|
||||
className="roomstamp-input"
|
||||
value={draft.name}
|
||||
onChange={(e) => patch({ name: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<AlignToggle
|
||||
value={draft.nameAlign}
|
||||
fallback="left"
|
||||
label={`${t("roomStamp.name")} — ${t("text.group")}`}
|
||||
onChange={(nameAlign) => patch({ nameAlign })}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label className="roomstamp-field">
|
||||
<span className="roomstamp-label">{t("roomStamp.nameLine2")}</span>
|
||||
<input
|
||||
type="text"
|
||||
className="roomstamp-input"
|
||||
value={draft.nameLine2 ?? ""}
|
||||
onChange={(e) => patch({ nameLine2: e.target.value })}
|
||||
<div className="roomstamp-row">
|
||||
<label className="roomstamp-field">
|
||||
<span className="roomstamp-label">{t("roomStamp.nameLine2")}</span>
|
||||
<input
|
||||
type="text"
|
||||
className="roomstamp-input"
|
||||
value={draft.nameLine2 ?? ""}
|
||||
onChange={(e) => patch({ nameLine2: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<AlignToggle
|
||||
value={draft.line2Align}
|
||||
fallback="left"
|
||||
disabled={!hasLine2}
|
||||
label={`${t("roomStamp.nameLine2")} — ${t("text.group")}`}
|
||||
onChange={(line2Align) => patch({ line2Align })}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label className="roomstamp-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={draft.showFloorArea}
|
||||
onChange={(e) => patch({ showFloorArea: e.target.checked })}
|
||||
<div className="roomstamp-row">
|
||||
<label className="roomstamp-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={draft.showFloorArea}
|
||||
onChange={(e) => patch({ showFloorArea: e.target.checked })}
|
||||
/>
|
||||
<span>{t("roomStamp.showFloorArea")}</span>
|
||||
</label>
|
||||
<AlignToggle
|
||||
value={draft.floorAreaAlign}
|
||||
fallback="center"
|
||||
disabled={!draft.showFloorArea}
|
||||
label={`${t("roomStamp.showFloorArea")} — ${t("text.group")}`}
|
||||
onChange={(floorAreaAlign) => patch({ floorAreaAlign })}
|
||||
/>
|
||||
<span>{t("roomStamp.showFloorArea")}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label className="roomstamp-field">
|
||||
<span className="roomstamp-label">{t("roomStamp.floorAreaPrefix")}</span>
|
||||
@@ -106,14 +183,23 @@ export function RoomStampEditor({ value, onApply, onCancel }: RoomStampEditorPro
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="roomstamp-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={draft.showUsage}
|
||||
onChange={(e) => patch({ showUsage: e.target.checked })}
|
||||
<div className="roomstamp-row">
|
||||
<label className="roomstamp-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={draft.showUsage}
|
||||
onChange={(e) => patch({ showUsage: e.target.checked })}
|
||||
/>
|
||||
<span>{t("roomStamp.showUsage")}</span>
|
||||
</label>
|
||||
<AlignToggle
|
||||
value={draft.usageAlign}
|
||||
fallback="center"
|
||||
disabled={!draft.showUsage}
|
||||
label={`${t("roomStamp.showUsage")} — ${t("text.group")}`}
|
||||
onChange={(usageAlign) => patch({ usageAlign })}
|
||||
/>
|
||||
<span>{t("roomStamp.showUsage")}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="texteditor-foot">
|
||||
|
||||
Reference in New Issue
Block a user