feat(quick-260922-ge2): XFrame-Widget - Ausschnitt der Seite waehlen und einpassen, Zoom fuer die ganze Seite, Nur anzeigen

- Resolver: crop (x/y/w/h, geklemmt auf 1280er-Seite, x verschoben statt abgewiesen), zoom (50..150, groesste Stufe <= n), readOnly (nur echtes true)
- xframe-crop.ts: reine Geometrie - computeCropLayout (contain + zentriert, scale 0 bis gemessen), applyCropDrag (Verschieben, vier Ecken, Gegenecke bleibt)
- Kachel: Clip + verschobener, skalierter <iframe> bei fester Layoutbreite 1280, ResizeObserver am Koerper; Zoom-Zweig mit Prozentmassen; 100 % wie bisher; readOnly-Flaeche nur im Ansichtsmodus
- Formular: Checkbox Ausschnitt (ein Aufruf mit crop + readOnly), Vorschau 1280 px breit mit derselben Sandbox und pointer-events none, Rahmen mit vier Griffen (Pointer-Events, Capture-Waechter fuer jsdom), Zahlenfelder, Zoom-Auswahl nur ohne Ausschnitt, Nur anzeigen, dauerhafter Hinweis
- Rahmen als <fieldset> statt div role=group (Biome useSemanticElements, kein biome-ignore)
- Test-Helfer stubResizeObserver, 13 neue Schluessel de/en, Ausschnitt auf der Umlaut-Allowlist; Web-Tests 604 -> 640

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-22 12:11:24 +02:00
parent 5aa577a7fa
commit 445b1d3100
12 changed files with 1110 additions and 31 deletions
+33
View File
@@ -0,0 +1,33 @@
import { vi } from 'vitest';
/**
* fake-resize-observer — file-scoped ResizeObserver stub with an explicit
* size (quick-260922-ge2).
*
* setup.ts already polyfills ResizeObserver globally for jsdom, but it
* always reports 1200x800. Components whose geometry depends on the
* measured size (XFrame crop clip, crop preview) need deterministic,
* test-chosen numbers — so a test file overrides the global with this
* stub and restores it via `vi.unstubAllGlobals()` in `afterEach`.
*
* The stub class is structurally a ResizeObserver (observe / unobserve /
* disconnect); the single `as ResizeObserverEntry` cast is enough because
* jsdom provides `DOMRectReadOnly` and the callback only reads
* `contentRect`; no double cast through `unknown` is needed.
*/
export function stubResizeObserver(size: { width: number; height: number }): void {
vi.stubGlobal(
'ResizeObserver',
class {
private readonly cb: ResizeObserverCallback;
constructor(cb: ResizeObserverCallback) {
this.cb = cb;
}
observe() {
this.cb([{ contentRect: new DOMRectReadOnly(0, 0, size.width, size.height) } as ResizeObserverEntry], this);
}
unobserve() {}
disconnect() {}
},
);
}