diff --git a/src/App.tsx b/src/App.tsx index 205f802..d1a2d3b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -419,6 +419,8 @@ export default function App() { setStampEditorRoomId, editTextId, setEditTextId, + editTextMinHeightM, + setEditTextMinHeightM, } = useContextMenuState(); // Gewählte Annotation auf einem offenen Layout-Blatt (Linie/Rechteck/Text/ @@ -801,7 +803,7 @@ export default function App() { // applyResult-Reihenfolge commit→focusDrawing). Auswahl-Löschen wie // onEditDrawingText (useMouseSelectionHandlers.ts), damit kein anderes // Element gleichzeitig markiert bleibt. - focusDrawing: (id) => { + focusDrawing: (id, minHeightM) => { setSelectedWallIds([]); setSelectedCeilingIds([]); setSelectedOpeningIds([]); @@ -813,6 +815,7 @@ export default function App() { setSelectedContextObjectIds([]); setSelectedSectionLineId(null); setSelectedDrawingId(id); + setEditTextMinHeightM(minHeightM ?? null); setEditTextId(id); }, }; @@ -2286,7 +2289,7 @@ export default function App() { selectedSectionLineId, setSelectedSectionLineId, }, - contextMenuState: { setMenu, setStampEditorRoomId, editTextId, setEditTextId }, + contextMenuState: { setMenu, setStampEditorRoomId, editTextId, setEditTextId, setEditTextMinHeightM }, }); // „Bild einfügen" (Werkzeug „image") — siehe state/useImportHandling.ts. @@ -2736,6 +2739,7 @@ export default function App() { onStampEdit={onOpenStampEditor} onEditDrawingText={onEditDrawingText} editTextId={editTextId} + editTextMinHeightM={editTextMinHeightM} onCommitTextEdit={commitTextEdit} onCloseTextEdit={closeTextEdit} onPlanSelect={onPlanSelect} diff --git a/src/commands/cmds/textbox.test.ts b/src/commands/cmds/textbox.test.ts index 97ed91a..58ead23 100644 --- a/src/commands/cmds/textbox.test.ts +++ b/src/commands/cmds/textbox.test.ts @@ -2,7 +2,9 @@ * `textboxCommand` — committet seit der Umstellung auf Inline-Editing (kein * Dialog/keine Befehlszeilen-Eingabe mehr, InDesign-artiges Textrahmen- * Verhalten, Nutzer-Wunsch) SOFORT ein leeres Text-Element MIT Spaltenbreite - * nach Anker+Breite und meldet `focusDrawingId`. + * nach einem aufgezogenen RECHTECK (zwei diagonale Ecken, nicht nur eine + * Breitenlinie) und meldet `focusDrawingId` + `focusMinHeightM` (die + * aufgezogene Rahmenhöhe als Mindesthöhe des Inline-Editors). */ import { describe, it, expect } from "vitest"; @@ -45,23 +47,25 @@ function makeCtx(p: Project): CommandContext { } describe("textboxCommand", () => { - it("committet nach Anker+Breite sofort ein leeres Text-Element mit width und meldet focusDrawingId", () => { + it("committet nach einem Rechteck (2 Ecken) ein leeres Text-Element mit width + meldet focusDrawingId/focusMinHeightM", () => { const p = project(); const ctx = makeCtx(p); - const [afterPoint] = textboxCommand.onInput( + const [afterFirst] = textboxCommand.onInput( textboxCommand.init(), - { kind: "point", point: { x: 0, y: 0 } }, + { kind: "point", point: { x: 0, y: 5 } }, ctx, ); + // Gegenecke: 3m breit, 1.5m hoch (Rahmen, keine reine Breitenlinie mehr). const [, result] = textboxCommand.onInput( - afterPoint, - { kind: "point", point: { x: 2, y: 0 } }, // Spaltenbreite = 2m + afterFirst, + { kind: "point", point: { x: 3, y: 3.5 } }, ctx, ); expect(result.done).toBe(true); expect(result.commit).toBeDefined(); expect(result.focusDrawingId).toBeDefined(); + expect(result.focusMinHeightM).toBeCloseTo(1.5, 9); const next = result.commit!(p); expect(next.drawings2d.length).toBe(1); @@ -70,7 +74,30 @@ describe("textboxCommand", () => { expect(d.geom.shape).toBe("text"); if (d.geom.shape === "text") { expect(d.geom.text).toBe(""); - expect(d.geom.width).toBeCloseTo(2, 9); // InDesign-artiger Textrahmen: Breite gesetzt + expect(d.geom.width).toBeCloseTo(3, 9); // InDesign-artiger Textrahmen: Breite gesetzt + expect(d.geom.at.x).toBeCloseTo(0, 9); // oben-links des Rahmens (unabhängig von der Zugrichtung) } }); + + it("beliebige Zugrichtung (Gegenecke oben-links vom Start): Rahmen wird trotzdem korrekt normalisiert", () => { + const p = project(); + const ctx = makeCtx(p); + const [afterFirst] = textboxCommand.onInput( + textboxCommand.init(), + { kind: "point", point: { x: 5, y: 2 } }, + ctx, + ); + const [, result] = textboxCommand.onInput( + afterFirst, + { kind: "point", point: { x: 2, y: 4 } }, // Gegenecke LINKS und OBEN vom Start + ctx, + ); + const next = result.commit!(p); + const d = next.drawings2d[0]; + if (d.geom.shape === "text") { + expect(d.geom.width).toBeCloseTo(3, 9); // |5-2| + expect(d.geom.at.x).toBeCloseTo(2, 9); // min(x) + } + expect(result.focusMinHeightM).toBeCloseTo(2, 9); // |4-2| + }); }); diff --git a/src/commands/cmds/textbox.ts b/src/commands/cmds/textbox.ts index a8e7336..804b87b 100644 --- a/src/commands/cmds/textbox.ts +++ b/src/commands/cmds/textbox.ts @@ -1,17 +1,23 @@ -// Textspalte (Absatztext) — wie das Text-Werkzeug, aber mit einer Spaltenbreite: -// der Text bricht beim Rendern UND beim Editieren wortweise auf diese Breite -// um (InDesign-artiges Textrahmen-Verhalten, Nutzer-Wunsch). Schritte: -// 1) „Ankerpunkt:" → Punkt (oben-links der Spalte) -// 2) „Spaltenbreite:" → Punkt (die horizontale Distanz zum Anker = Breite; -// Live-Vorschau der Breitenlinie) +// Textspalte (Absatztext) — wie das Text-Werkzeug, aber als aufgezogener +// RAHMEN: Breite UND Höhe kommen aus einem Rechteck (zwei diagonale Punkte), +// nicht nur einer Breitenlinie (InDesign-artiges Textrahmen-Verhalten, +// Nutzer-Wunsch). Schritte: +// 1) „Erste Ecke:" → Punkt +// 2) „Gegenecke:" → Punkt (Live-Vorschau des Rahmens, beliebige Richtung) // Committet danach SOFORT ein leeres Text-Element (mit `width`) und meldet -// `focusDrawingId` — die UI (PlanView) öffnet den Inline-Rich-Text-Editor -// direkt auf der Zeichenfläche, mit der Spaltenbreite als CSS-Breite (echter -// Wortumbruch beim Tippen, kein Dialog). Bleibt der Editor leer, verwirft -// `commitTextEdit` (App.tsx) das Element wieder. +// `focusDrawingId` + `focusMinHeightM` (die aufgezogene Rahmenhöhe als +// MINDESThöhe, s. types.ts) — die UI (PlanView) öffnet den Inline-Rich- +// Text-Editor direkt auf der Zeichenfläche, mit der Spaltenbreite als CSS- +// Breite (echter Wortumbruch beim Tippen, kein Dialog) und dem Rahmen als +// Mindesthöhe (wächst bei mehr Text darüber hinaus). Bleibt der Editor +// leer, verwirft `commitTextEdit`/`closeTextEdit` (App.tsx) das Element +// wieder. // -// Reuse des bestehenden {shape:"text"}-Elements (mit `width`) — so erben Selektion, -// Verschieben und Griff automatisch vom Einzeltext; nur die Breite kommt hinzu. +// Reuse des bestehenden {shape:"text"}-Elements (mit `width`) — so erben +// Selektion, Verschieben und Griff automatisch vom Einzeltext; die Modell- +// Geometrie selbst kennt aber keine feste Rahmenhöhe (nur `width` für den +// Wortumbruch) — die Höhe bleibt reine Editor-Startgrösse (PlanView-lokal, +// s. focusMinHeightM), die Textmenge bestimmt die tatsächliche Höhe. import type { Drawing2D } from "../../model/types"; import { uniqueId } from "../../tools/types"; @@ -20,39 +26,61 @@ import type { CommandContext, CommandResult, CommandState, + DraftShape, Project, - ToolDraft, Vec2, } from "../types"; -/** Default-Schrifthöhe einer Textspalte in Modell-Metern. */ +/** Default-Schrifthöhe einer Textspalte in Modell-Metern (= Baseline-Versatz vom Rahmen-Top). */ const DEFAULT_TEXT_HEIGHT_M = 0.25; -/** Kleinste sinnvolle Spaltenbreite (Meter). */ -const MIN_WIDTH_M = 0.2; +/** Kleinste sinnvolle Rahmen-Breite/-Höhe (Meter). */ +const MIN_SIZE_M = 0.2; -interface TbPoint extends CommandState { - phase: "point"; +interface TbFirst extends CommandState { + phase: "first"; } -interface TbWidth extends CommandState { - phase: "width"; - at: Vec2; +interface TbSecond extends CommandState { + phase: "second"; + a: Vec2; cursor: Vec2 | null; } -type TbState = TbPoint | TbWidth; +type TbState = TbFirst | TbSecond; -/** Horizontale Spaltenbreite aus Anker + Cursor (Betrag der x-Differenz, geklemmt). */ -function widthOf(at: Vec2, cursor: Vec2): number { - return Math.max(MIN_WIDTH_M, Math.abs(cursor.x - at.x)); +/** Normalisiertes Rechteck aus zwei diagonalen Punkten (beliebige Zugrichtung). */ +function rectOf(a: Vec2, b: Vec2): { minX: number; maxX: number; minY: number; maxY: number } { + return { + minX: Math.min(a.x, b.x), + maxX: Math.max(a.x, b.x), + minY: Math.min(a.y, b.y), + maxY: Math.max(a.y, b.y), + }; } -/** Vorschau: waagrechte Breitenlinie am Anker (zeigt die Spaltenbreite). */ -function widthDraft(at: Vec2, cursor: Vec2): ToolDraft { - const w = widthOf(at, cursor); - const b: Vec2 = { x: at.x + w, y: at.y }; +/** Breite/Höhe (geklemmt auf MIN_SIZE_M) + die Textanker-Position (oben-links, als Baseline). */ +function frameOf(a: Vec2, b: Vec2): { at: Vec2; width: number; heightM: number } { + const r = rectOf(a, b); + const width = Math.max(MIN_SIZE_M, r.maxX - r.minX); + const heightM = Math.max(MIN_SIZE_M, r.maxY - r.minY); + // `at` ist die Baseline-links der ERSTEN Zeile (s. drawing2d.ts/primitives.tsx); + // die Rahmen-Oberkante liegt eine Zeilenhöhe darüber → at.y = Rahmen-Top − Zeilenhöhe. + return { at: { x: r.minX, y: r.maxY - DEFAULT_TEXT_HEIGHT_M }, width, heightM }; +} + +/** Vorschau: Rahmen-Rechteck zwischen den beiden Ecken. */ +function frameDraft(a: Vec2, cursor: Vec2): { preview: DraftShape[]; vertices: Vec2[]; hud: { at: Vec2; text: string } } { + const r = rectOf(a, cursor); + const pts: Vec2[] = [ + { x: r.minX, y: r.minY }, + { x: r.maxX, y: r.minY }, + { x: r.maxX, y: r.maxY }, + { x: r.minX, y: r.maxY }, + ]; + const w = Math.max(MIN_SIZE_M, r.maxX - r.minX); + const h = Math.max(MIN_SIZE_M, r.maxY - r.minY); return { - preview: [{ kind: "line", a: at, b }], - vertices: [at, b], - hud: { at: b, text: `${w.toFixed(2)} m` }, + preview: [{ kind: "poly", pts, closed: true }], + vertices: [a, cursor], + hud: { at: cursor, text: `${w.toFixed(2)} × ${h.toFixed(2)} m` }, }; } @@ -68,45 +96,45 @@ function appendEmptyTextbox(p: Project, id: string, at: Vec2, width: number, ctx } const idle = (): [CommandState, CommandResult] => [ - { phase: "point", lastPoint: null } as TbPoint, + { phase: "first", lastPoint: null } as TbFirst, { draft: null, done: true }, ]; export const textboxCommand: Command = { name: "textbox", labelKey: "cmd.textbox.label", - prompt: (s) => ((s as TbState).phase === "width" ? "cmd.textbox.width" : "cmd.textbox.point"), + prompt: (s) => ((s as TbState).phase === "second" ? "cmd.textbox.second" : "cmd.textbox.first"), accepts: () => ["point"], options: () => [], - init: (): TbPoint => ({ phase: "point", lastPoint: null }), + init: (): TbFirst => ({ phase: "first", lastPoint: null }), onInput: (state, input, ctx): [CommandState, CommandResult] => { const s = state as TbState; if (input.kind !== "point") return [s, { draft: null }]; - if (s.phase === "point") { - const ns: TbWidth = { phase: "width", at: input.point, cursor: input.point, lastPoint: input.point }; - return [ns, { draft: widthDraft(input.point, input.point) }]; + if (s.phase === "first") { + const ns: TbSecond = { phase: "second", a: input.point, cursor: input.point, lastPoint: input.point }; + return [ns, { draft: { preview: [], vertices: [input.point] } }]; } - // phase === "width" - const width = widthOf(s.at, input.point); + // phase === "second" + const { at, width, heightM } = frameOf(s.a, input.point); const id = uniqueId("dr2d"); - const at = s.at; return [ - { phase: "point", lastPoint: null } as TbPoint, + { phase: "first", lastPoint: null } as TbFirst, { draft: null, done: true, commit: (p) => appendEmptyTextbox(p, id, at, width, ctx), focusDrawingId: id, + focusMinHeightM: heightM, }, ]; }, onMove: (state, point): [CommandState, CommandResult] => { const s = state as TbState; - if (s.phase !== "width") return [s, { draft: null }]; - const ns: TbWidth = { ...s, cursor: point }; - return [ns, { draft: widthDraft(s.at, point) }]; + if (s.phase !== "second") return [s, { draft: null }]; + const ns: TbSecond = { ...s, cursor: point }; + return [ns, { draft: frameDraft(s.a, point) }]; }, onConfirm: (): [CommandState, CommandResult] => idle(), diff --git a/src/commands/engine.ts b/src/commands/engine.ts index 0c54491..0aa97b9 100644 --- a/src/commands/engine.ts +++ b/src/commands/engine.ts @@ -54,10 +54,10 @@ export interface EngineHost { isFloor(): boolean; /** * Optional: ein frisch erzeugtes Drawing2D-Element fokussieren (Passthrough - * von `CommandResult.focusDrawingId`, z. B. um sofort den Inline-Text-Editor - * zu öffnen). Fehlt der Host-Handler, ist das ein No-op. + * von `CommandResult.focusDrawingId`/`focusMinHeightM`, z. B. um sofort den + * Inline-Text-Editor zu öffnen). Fehlt der Host-Handler, ist das ein No-op. */ - focusDrawing?(drawingId: string): void; + focusDrawing?(drawingId: string, minHeightM?: number): void; } /** Öffentlicher Schnappschuss für die Command-Line-UI (rein lesend). */ @@ -482,7 +482,7 @@ export class CommandEngine { private applyResult(res: CommandResult): void { if (res.commit) this.host.commit(res.commit); - if (res.focusDrawingId) this.host.focusDrawing?.(res.focusDrawingId); + if (res.focusDrawingId) this.host.focusDrawing?.(res.focusDrawingId, res.focusMinHeightM); if (res.draft) this.host.setDraft(res.draft); else this.host.setDraft(null); if (res.done) { diff --git a/src/commands/types.ts b/src/commands/types.ts index 77ca485..6e28cb5 100644 --- a/src/commands/types.ts +++ b/src/commands/types.ts @@ -116,6 +116,13 @@ export interface CommandResult { * die Engine selbst wertet die ID nicht aus. */ focusDrawingId?: string; + /** + * Nur zusammen mit `focusDrawingId`: die vom Nutzer aufgezogene Rahmenhöhe + * (Meter) als MINDESTHöhe des Inline-Editors (InDesign-Textrahmen — wächst + * bei Bedarf über diese Höhe hinaus, schrumpft aber nicht darunter). Fehlt + * sie, bestimmt allein der Inhalt die Höhe (Text-Werkzeug ohne Rahmen). + */ + focusMinHeightM?: number; } // ── Befehls-Zustand ────────────────────────────────────────────────────────── diff --git a/src/i18n/de.ts b/src/i18n/de.ts index 6f07a3b..c8a3ffb 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -1141,11 +1141,9 @@ export const de = { "cmd.arc.end": "Endpunkt (Endwinkel):", "cmd.text.label": "Text", "cmd.text.point": "Ankerpunkt:", - "cmd.text.enter": "Text eingeben:", "cmd.textbox.label": "Textspalte", - "cmd.textbox.point": "Ankerpunkt:", - "cmd.textbox.width": "Spaltenbreite (zweiter Punkt):", - "cmd.textbox.enter": "Text eingeben:", + "cmd.textbox.first": "Erste Ecke:", + "cmd.textbox.second": "Gegenecke (Breite × Höhe):", // Editierbefehle (Tier 1): Move/Copy/Offset operieren auf der Auswahl. "cmd.move.label": "Verschieben", "cmd.move.base": "Basispunkt:", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 42577f4..0b10a1c 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -1130,11 +1130,9 @@ export const en: Record = { "cmd.arc.end": "End point (end angle):", "cmd.text.label": "Text", "cmd.text.point": "Anchor point:", - "cmd.text.enter": "Enter text:", "cmd.textbox.label": "Text column", - "cmd.textbox.point": "Anchor point:", - "cmd.textbox.width": "Column width (second point):", - "cmd.textbox.enter": "Enter text:", + "cmd.textbox.first": "First corner:", + "cmd.textbox.second": "Opposite corner (width × height):", "cmd.move.label": "Move", "cmd.move.base": "Base point:", "cmd.move.target": "Target point:", diff --git a/src/menus/useContextMenuState.ts b/src/menus/useContextMenuState.ts index a7004d7..86ea5df 100644 --- a/src/menus/useContextMenuState.ts +++ b/src/menus/useContextMenuState.ts @@ -27,6 +27,11 @@ export function useContextMenuState() { const [stampEditorRoomId, setStampEditorRoomId] = useState(null); // Text-Inhalt-Editor: Id des per Doppelklick bearbeiteten Text-2D-Elements. const [editTextId, setEditTextId] = useState(null); + // Mindesthöhe (Meter) des Inline-Editors, wenn `editTextId` aus einem frisch + // aufgezogenen Textspalten-Rahmen kommt (s. textbox.ts focusMinHeightM); + // null = keine Mindesthöhe (Text-Werkzeug bzw. Doppelklick-Edit bestehender + // Elemente — dort bestimmt allein der Inhalt die Höhe). + const [editTextMinHeightM, setEditTextMinHeightM] = useState(null); return { layerClipboard, @@ -41,5 +46,7 @@ export function useContextMenuState() { setStampEditorRoomId, editTextId, setEditTextId, + editTextMinHeightM, + setEditTextMinHeightM, }; } diff --git a/src/plan/PlanView.tsx b/src/plan/PlanView.tsx index fbfcb47..8afb0b9 100644 --- a/src/plan/PlanView.tsx +++ b/src/plan/PlanView.tsx @@ -313,6 +313,12 @@ export interface PlanViewProps { * Ausschnitt. */ editTextId?: string | null; + /** + * Mindesthöhe (Meter) des Inline-Editors, wenn `editTextId` aus einem + * frisch aufgezogenen Textspalten-RAHMEN kommt (s. textbox.ts). `null`/ + * undefined = keine Mindesthöhe, der Inhalt bestimmt die Höhe allein. + */ + editTextMinHeightM?: number | null; /** Jede Änderung im Inline-Text-Editor (live, pro Tastendruck). */ onCommitTextEdit?: (doc: RichTextDoc) => void; /** Inline-Text-Editor schliessen (Backdrop-Klick/Escape). */ @@ -445,6 +451,7 @@ export const PlanView = forwardRef( onStampEdit, onEditDrawingText, editTextId, + editTextMinHeightM, onCommitTextEdit, onCloseTextEdit, onOpenSectionLevel, @@ -2494,7 +2501,18 @@ export const PlanView = forwardRef( const screen = vbToClient(topAnchor.x, topAnchor.y); const FONT_PX_AT_12PT = 16; // 12pt @ 96dpi (RichTextEditor-Default, s. LayoutSheet.tsx). const scale = (prim.heightM * pxPerM) / FONT_PX_AT_12PT; - const widthPx = prim.wrapWidth ? (prim.wrapWidth * pxPerM) / scale : undefined; + // MIT wrapWidth (Textspalte): feste Breite, Editor bricht live um (nowrap + // AUS). OHNE (Text-Werkzeug): keine feste Breite — `.planview-inline-text + // .rt-surface` steht auf nowrap (s. styles.css), die Box wächst mit jedem + // Zeichen; eine Mindestbreite verhindert eine unsichtbare 0px-Box beim + // leeren Start (der eigentliche Grund, warum der Anker bisher unsichtbar + // blieb — `width:undefined` kollabierte auf shrink-to-fit von NICHTS). + const hasWrap = !!prim.wrapWidth; + const widthPx = hasWrap ? (prim.wrapWidth! * pxPerM) / scale : undefined; + const minWidthPx = hasWrap ? undefined : 160; + // Aufgezogener Textspalten-Rahmen (textbox.ts): dessen Höhe als + // Mindesthöhe des Editors — wächst bei mehr Text darüber hinaus. + const minHeightPx = editTextMinHeightM ? (editTextMinHeightM * pxPerM) / scale : undefined; return ( <>
( onPointerDown={() => onCloseTextEdit?.()} />
; contextMenuState: Pick< ReturnType, - "setMenu" | "setStampEditorRoomId" | "editTextId" | "setEditTextId" + "setMenu" | "setStampEditorRoomId" | "editTextId" | "setEditTextId" | "setEditTextMinHeightM" >; }) { const { project, setProject, activeLevelId, visibleCodes, categoryDisplay, selectionState, contextMenuState } = opts; @@ -80,7 +80,7 @@ export function useMouseSelectionHandlers(opts: { selectedSectionLineId, setSelectedSectionLineId, } = selectionState; - const { setMenu, setStampEditorRoomId, editTextId, setEditTextId } = contextMenuState; + const { setMenu, setStampEditorRoomId, editTextId, setEditTextId, setEditTextMinHeightM } = contextMenuState; // ── Maus-Schema: Auswahl + Kontextmenü aus Grundriss / 3D ───────────────── // Links-Klick im Grundriss: genau eine Wand wählen (Menge ersetzen) bzw. @@ -116,6 +116,10 @@ export function useMouseSelectionHandlers(opts: { setSelectedRoofIds([]); setSelectedContextObjectIds([]); setSelectedSectionLineId(null); + // Bestehendes Element per Doppelklick: keine Rahmen-Mindesthöhe (nur bei + // frisch aufgezogener Textspalte relevant, s. textbox.ts focusMinHeightM) + // — sonst könnte eine vorige Textspalten-Erstellung hier nachwirken. + setEditTextMinHeightM(null); setEditTextId(drawingId); }; @@ -155,6 +159,7 @@ export function useMouseSelectionHandlers(opts: { }); } setEditTextId(null); + setEditTextMinHeightM(null); }; // Kategorie-Code des getroffenen Elements (für die Sperr-Prüfung unten) — // nur die Kanäle, deren Modell-Typ eine categoryCode trägt (ExtrudedSolid/ diff --git a/src/styles.css b/src/styles.css index d2b8ae9..ca43546 100644 --- a/src/styles.css +++ b/src/styles.css @@ -6793,6 +6793,16 @@ body { min-height: 1.4em; padding: 2px 4px; } +/* Text-Werkzeug ohne Spaltenbreite: kein Wortumbruch — die Box wächst in der + Breite mit dem Inhalt (Enter erzeugt trotzdem weitere Zeilen), statt am + Editor-Rand umzubrechen wie die Textspalte. */ +.planview-inline-text-free .rt-editor, +.planview-inline-text-free .rt-surface { + width: max-content; +} +.planview-inline-text-free .rt-surface { + white-space: pre; +} /* HUD (Eigenschaften des gewählten VP) unten links. */ .layout-sheet-hud { diff --git a/src/views/viewportContent.tsx b/src/views/viewportContent.tsx index 8b54347..bf9a561 100644 --- a/src/views/viewportContent.tsx +++ b/src/views/viewportContent.tsx @@ -93,6 +93,7 @@ export function Content({ onStampEdit, onEditDrawingText, editTextId, + editTextMinHeightM, onCommitTextEdit, onCloseTextEdit, onPlanSelect, @@ -182,6 +183,7 @@ export function Content({ onStampEdit: (roomId: string) => void; onEditDrawingText?: (drawingId: string) => void; editTextId?: string | null; + editTextMinHeightM?: number | null; onCommitTextEdit?: (doc: RichTextDoc) => void; onCloseTextEdit?: () => void; onPlanSelect: (sel: PlanSelection) => void; @@ -307,6 +309,7 @@ export function Content({ onStampEdit={onStampEdit} onEditDrawingText={onEditDrawingText} editTextId={editTextId} + editTextMinHeightM={editTextMinHeightM} onCommitTextEdit={onCommitTextEdit} onCloseTextEdit={onCloseTextEdit} onOpenSectionLevel={onSelectLevel} @@ -369,6 +372,7 @@ export function Content({ onStampEdit={onStampEdit} onEditDrawingText={onEditDrawingText} editTextId={editTextId} + editTextMinHeightM={editTextMinHeightM} onCommitTextEdit={onCommitTextEdit} onCloseTextEdit={onCloseTextEdit} onOpenSectionLevel={onSelectLevel} @@ -473,6 +477,7 @@ export function Content({ onStampEdit={onStampEdit} onEditDrawingText={onEditDrawingText} editTextId={editTextId} + editTextMinHeightM={editTextMinHeightM} onCommitTextEdit={onCommitTextEdit} onCloseTextEdit={onCloseTextEdit} onOpenSectionLevel={onSelectLevel} @@ -525,6 +530,7 @@ function LevelPlanView({ onStampEdit, onEditDrawingText, editTextId, + editTextMinHeightM, onCommitTextEdit, onCloseTextEdit, onOpenSectionLevel, @@ -570,6 +576,7 @@ function LevelPlanView({ onStampEdit: (roomId: string) => void; onEditDrawingText?: (drawingId: string) => void; editTextId?: string | null; + editTextMinHeightM?: number | null; onCommitTextEdit?: (doc: RichTextDoc) => void; onCloseTextEdit?: () => void; onOpenSectionLevel?: (id: string) => void; @@ -654,6 +661,7 @@ function LevelPlanView({ onStampEdit={onStampEdit} onEditDrawingText={onEditDrawingText} editTextId={editTextId} + editTextMinHeightM={editTextMinHeightM} onCommitTextEdit={onCommitTextEdit} onCloseTextEdit={onCloseTextEdit} onOpenSectionLevel={onOpenSectionLevel} @@ -713,6 +721,7 @@ function SectionPlanView({ onStampEdit, onEditDrawingText, editTextId, + editTextMinHeightM, onCommitTextEdit, onCloseTextEdit, onOpenSectionLevel, @@ -753,6 +762,7 @@ function SectionPlanView({ onStampEdit: (roomId: string) => void; onEditDrawingText?: (drawingId: string) => void; editTextId?: string | null; + editTextMinHeightM?: number | null; onCommitTextEdit?: (doc: RichTextDoc) => void; onCloseTextEdit?: () => void; onOpenSectionLevel?: (id: string) => void; @@ -863,6 +873,7 @@ function SectionPlanView({ onStampEdit={onStampEdit} onEditDrawingText={onEditDrawingText} editTextId={editTextId} + editTextMinHeightM={editTextMinHeightM} onCommitTextEdit={onCommitTextEdit} onCloseTextEdit={onCloseTextEdit} onOpenSectionLevel={onOpenSectionLevel}