2D: Inline-Text-Editor-Nachbesserung (unsichtbarer Anker) + Textspalte als echtes Rechteck
Nutzer-Report nach der Umstellung auf Inline-Editing: beides funktionierte nicht — "Text" liess sich kein Ankerpunkt setzen, "Textspalte" sollte ein Rechteck statt nur eine Breite sein. Root Cause für "Text": `.planview-inline-text` bekam ohne Spaltenbreite `width: undefined` — bei position:fixed ohne rechten Rand greift shrink-to- fit-Sizing, das für ein LEERES contentEditable auf ~0px kollabiert. Der Klick erzeugte tatsächlich ein Element + öffnete den Editor, beides war nur unsichtbar (eine 0px-Box lässt sich nicht anklicken/fokussieren). Fix: neue Klasse .planview-inline-text-free (kein wrapWidth) erzwingt `width: max-content` + `white-space: pre` auf Editor/Surface (Box wächst mit dem Inhalt statt umzubrechen — "Text" bleibt einzeilig bis Enter, wie gewünscht) plus eine CSS-Mindestbreite (160px) für den leeren Startzustand. "Textspalte" (textbox.ts) zeichnet jetzt ein RECHTECK (zwei diagonale Ecken, beliebige Zugrichtung) statt nur eine horizontale Breitenlinie — die aufgezogene Höhe wird nicht im Modell gespeichert (das Textformat kennt nur `width` für den Wortumbruch, keine feste Rahmenhöhe), sondern als Editor-MINDESThöhe durchgereicht: CommandResult/EngineHost.focusDrawing um `focusMinHeightM` erweitert (Passthrough wie focusDrawingId), App.tsx hält sie in einem neuen editTextMinHeightM-State (useContextMenuState.ts), PlanView nutzt sie als CSS-min-height des Editors — der Rahmen wächst bei mehr Text darüber hinaus, schrumpft aber nicht darunter (InDesign-Verhalten). Beim Doppelklick-Editieren bestehender Elemente wird die Mindesthöhe zurückgesetzt (kein Nachwirken einer vorigen Textspalten-Erstellung). textbox.test.ts an die neue Rechteck-Geste angepasst (+ Test für beliebige Zugrichtung). tsc -b / vitest run (919/919) / npm run build grün. Die eigentliche Sichtbarkeits-Vermutung (CSS-Kollaps) konnte ich nicht interaktiv im Browser verifizieren (kein Browser-Tool verfügbar) — Nutzer prüft erneut in der laufenden App.
This commit is contained in:
@@ -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|
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user