fix(quick-260922-ge2): Rahmenhoehe in der Kachel = Vorschauhoehe, Vorschau ohne Querbalken
Befund aus dem Browser-Rundgang: example.com setzt `margin: 15vh` — mit 3000 px Vorschauhoehe lag die Ueberschrift bei y 450, in der Kachel mit 720 px Rahmenhoehe bei y 108. Der in der Vorschau gewaehlte Ausschnitt zeigte in der Kachel also etwas anderes. Die Kachel nutzt jetzt dieselbe Layouthoehe wie die Vorschau (3000), damit vh-relative Seiten identisch umbrechen; der Rest wird ohnehin weggeschnitten. Vorschau: `overflow-x-hidden`, die Eckgriffe am rechten Rand erzeugten einen 4-px-Querbalken. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,11 +15,11 @@ const START = { x: 100, y: 100, w: 400, h: 300 };
|
|||||||
|
|
||||||
describe('computeCropLayout (quick-260922-ge2)', () => {
|
describe('computeCropLayout (quick-260922-ge2)', () => {
|
||||||
it('Test 1: breitenbegrenzt — 1280x720 in 640x720 -> scale 0.5, left 0, top 180', () => {
|
it('Test 1: breitenbegrenzt — 1280x720 in 640x720 -> scale 0.5, left 0, top 180', () => {
|
||||||
expect(computeCropLayout(FULL, 640, 720)).toEqual({ scale: 0.5, left: 0, top: 180, frameHeight: 720 });
|
expect(computeCropLayout(FULL, 640, 720)).toEqual({ scale: 0.5, left: 0, top: 180, frameHeight: 3000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Test 2: hoehenbegrenzt — 1280x720 in 1280x180 -> scale 0.25, left 480, top 0', () => {
|
it('Test 2: hoehenbegrenzt — 1280x720 in 1280x180 -> scale 0.25, left 480, top 0', () => {
|
||||||
expect(computeCropLayout(FULL, 1280, 180)).toEqual({ scale: 0.25, left: 480, top: 0, frameHeight: 720 });
|
expect(computeCropLayout(FULL, 1280, 180)).toEqual({ scale: 0.25, left: 480, top: 0, frameHeight: 3000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Test 3: Vergroesserung erlaubt — 200x100 in 800x400 -> scale 4', () => {
|
it('Test 3: Vergroesserung erlaubt — 200x100 in 800x400 -> scale 4', () => {
|
||||||
@@ -27,18 +27,18 @@ describe('computeCropLayout (quick-260922-ge2)', () => {
|
|||||||
scale: 4,
|
scale: 4,
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
frameHeight: 720,
|
frameHeight: 3000,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Test 4: frameHeight mindestens 720, sonst y + h', () => {
|
it('Test 4: frameHeight ist immer die Vorschauhoehe 3000 — gleiches Layout wie in der Vorschau (vh-Masse)', () => {
|
||||||
expect(computeCropLayout({ x: 0, y: 0, w: 1280, h: 100 }, 640, 360).frameHeight).toBe(720);
|
expect(computeCropLayout({ x: 0, y: 0, w: 1280, h: 100 }, 640, 360).frameHeight).toBe(3000);
|
||||||
expect(computeCropLayout({ x: 0, y: 3000, w: 1280, h: 400 }, 640, 360).frameHeight).toBe(3400);
|
expect(computeCropLayout({ x: 0, y: 3000, w: 1280, h: 400 }, 640, 360).frameHeight).toBe(3000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Test 5: ungemessene Kachel (0x0 oder 640x0) -> scale 0, left 0, top 0 (nichts rendern)', () => {
|
it('Test 5: ungemessene Kachel (0x0 oder 640x0) -> scale 0, left 0, top 0 (nichts rendern)', () => {
|
||||||
expect(computeCropLayout(FULL, 0, 0)).toEqual({ scale: 0, left: 0, top: 0, frameHeight: 720 });
|
expect(computeCropLayout(FULL, 0, 0)).toEqual({ scale: 0, left: 0, top: 0, frameHeight: 3000 });
|
||||||
expect(computeCropLayout(FULL, 640, 0)).toEqual({ scale: 0, left: 0, top: 0, frameHeight: 720 });
|
expect(computeCropLayout(FULL, 640, 0)).toEqual({ scale: 0, left: 0, top: 0, frameHeight: 3000 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,14 @@
|
|||||||
* der beiden Massstaebe (darf > 1 sein: ein kleiner Ausschnitt in einer
|
* der beiden Massstaebe (darf > 1 sein: ein kleiner Ausschnitt in einer
|
||||||
* grossen Kachel wird vergroessert) und die Zentrierung im Rest.
|
* grossen Kachel wird vergroessert) und die Zentrierung im Rest.
|
||||||
*
|
*
|
||||||
* `frameHeight` ist mindestens 720: die eingebettete Seite braucht eine
|
* `frameHeight` ist IMMER die Vorschauhoehe (3000 Seitenpixel), nicht
|
||||||
* Layouthoehe, sonst rendern viele Seiten ihren Inhalt nicht oder brechen
|
* "Unterkante des Ausschnitts": viele Seiten setzen Masse relativ zur
|
||||||
* ihn anders um; liegt der Ausschnitt tiefer, reicht der Rahmen bis zu
|
* Fensterhoehe (`vh`, `100vh`-Zentrierung, `margin: 15vh` bei example.com).
|
||||||
* seiner Unterkante.
|
* Haetten Vorschau und Kachel verschiedene Rahmenhoehen, laege derselbe
|
||||||
|
* Inhalt an verschiedenen Stellen — der in der Vorschau gewaehlte Ausschnitt
|
||||||
|
* zeigte in der Kachel etwas anderes (Browser-Rundgang 22.09.2026: Ueberschrift
|
||||||
|
* in der Vorschau bei y 450, in der Kachel mit 720 px Rahmen bei y 108).
|
||||||
|
* Gleiche Hoehe = gleiches Layout; der Rest wird ohnehin weggeschnitten.
|
||||||
*
|
*
|
||||||
* Ziehmathematik der Vorschau: `move` verschiebt bei fester Groesse und
|
* Ziehmathematik der Vorschau: `move` verschiebt bei fester Groesse und
|
||||||
* klemmt an den Seitenraendern; die Eckmodi bewegen nur die zwei Kanten
|
* klemmt an den Seitenraendern; die Eckmodi bewegen nur die zwei Kanten
|
||||||
@@ -29,10 +33,12 @@ import {
|
|||||||
clampXframeCrop,
|
clampXframeCrop,
|
||||||
} from './xframe-config';
|
} from './xframe-config';
|
||||||
|
|
||||||
/** Hoehe der Vorschau-Seite in Seitenpixeln (Vorschau und Ziehen); 4000 bleibt die Speichergrenze. */
|
/**
|
||||||
|
* Hoehe der Seite in Seitenpixeln — in der Vorschau (Stage, Ziehen) UND als
|
||||||
|
* Layouthoehe des Rahmens in der Kachel (siehe Kopfkommentar); 4000 bleibt
|
||||||
|
* die Speichergrenze fuer `crop.y + crop.h`.
|
||||||
|
*/
|
||||||
export const XFRAME_PREVIEW_PAGE_HEIGHT = 3000;
|
export const XFRAME_PREVIEW_PAGE_HEIGHT = 3000;
|
||||||
/** Mindest-Layouthoehe des Rahmens im Ausschnitt-Modus. */
|
|
||||||
const MIN_FRAME_HEIGHT = 720;
|
|
||||||
|
|
||||||
export type XframeDragMode = 'move' | 'nw' | 'ne' | 'sw' | 'se';
|
export type XframeDragMode = 'move' | 'nw' | 'ne' | 'sw' | 'se';
|
||||||
|
|
||||||
@@ -45,7 +51,7 @@ export interface XframeCropLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function computeCropLayout(crop: XframeCrop, tileW: number, tileH: number): XframeCropLayout {
|
export function computeCropLayout(crop: XframeCrop, tileW: number, tileH: number): XframeCropLayout {
|
||||||
const frameHeight = Math.max(crop.y + crop.h, MIN_FRAME_HEIGHT);
|
const frameHeight = XFRAME_PREVIEW_PAGE_HEIGHT;
|
||||||
if (tileW <= 0 || tileH <= 0) return { scale: 0, left: 0, top: 0, frameHeight };
|
if (tileW <= 0 || tileH <= 0) return { scale: 0, left: 0, top: 0, frameHeight };
|
||||||
const scale = Math.min(tileW / crop.w, tileH / crop.h);
|
const scale = Math.min(tileW / crop.w, tileH / crop.h);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ describe('XframeWidget — Ausschnitt, Zoom, readOnly (quick-260922-ge2)', () =>
|
|||||||
expect(f.style.left).toBe('0px');
|
expect(f.style.left).toBe('0px');
|
||||||
expect(f.style.top).toBe('0px');
|
expect(f.style.top).toBe('0px');
|
||||||
expect(f.style.width).toBe('1280px');
|
expect(f.style.width).toBe('1280px');
|
||||||
expect(f.style.height).toBe('720px');
|
expect(f.style.height).toBe('3000px');
|
||||||
expect(f.style.transform).toBe('scale(0.5)');
|
expect(f.style.transform).toBe('scale(0.5)');
|
||||||
expect(f.style.transformOrigin).toBe('0 0');
|
expect(f.style.transformOrigin).toBe('0 0');
|
||||||
expect(f.className).not.toContain('h-full');
|
expect(f.className).not.toContain('h-full');
|
||||||
@@ -201,7 +201,7 @@ describe('XframeWidget — Ausschnitt, Zoom, readOnly (quick-260922-ge2)', () =>
|
|||||||
const f = frame();
|
const f = frame();
|
||||||
expect(f.style.left).toBe('-160px');
|
expect(f.style.left).toBe('-160px');
|
||||||
expect(f.style.top).toBe('-80px');
|
expect(f.style.top).toBe('-80px');
|
||||||
expect(f.style.height).toBe('720px');
|
expect(f.style.height).toBe('3000px');
|
||||||
expect(f.style.transform).toBe('scale(0.8)');
|
expect(f.style.transform).toBe('scale(0.8)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ function CropPreview({ url, crop, onCommit, rectangleLabel, emptyText }: CropPre
|
|||||||
<div
|
<div
|
||||||
ref={previewRef}
|
ref={previewRef}
|
||||||
data-testid="xframe-crop-preview"
|
data-testid="xframe-crop-preview"
|
||||||
className="relative overflow-y-auto rounded border border-border bg-background"
|
className="relative overflow-x-hidden overflow-y-auto rounded border border-border bg-background"
|
||||||
style={{ height: PREVIEW_HEIGHT_PX }}
|
style={{ height: PREVIEW_HEIGHT_PX }}
|
||||||
>
|
>
|
||||||
{url === null ? (
|
{url === null ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user