"use client" import { useEffect, useEffectEvent, useRef, useState } from "react" import { MonitorIcon, SmartphoneIcon, TabletIcon } from "lucide-react" import { cn } from "@/lib/utils" import { DEVICES, PREVIEW_CONTENT, PREVIEW_FOCUS, PREVIEW_READY, type Device } from "../../../lib/preview" import type { TemplateContent } from "../../../lib/types" const icons = { desktop: MonitorIcon, tablet: TabletIcon, mobile: SmartphoneIcon } // Die Vorlage läuft in echter Gerätebreite im iframe (damit Media Queries greifen) und wird // passend herunterskaliert. Änderungen gehen per postMessage hinein, ohne Neuladen. // `focus` (Baukasten): Baustein, zu dem die Vorschau scrollen soll; `n` erlaubt erneutes Springen zum selben Baustein. export function PreviewFrame({ templateKey, variantId, content, focus }: { templateKey: string variantId: string content: TemplateContent focus?: { id: string; n: number } | null }) { const frameRef = useRef(null) const containerRef = useRef(null) const [size, setSize] = useState({ width: 0, height: 0 }) const [device, setDevice] = useState("desktop") const post = useEffectEvent(() => { frameRef.current?.contentWindow?.postMessage({ type: PREVIEW_CONTENT, content }, window.location.origin) }) useEffect(() => { post() }, [content]) useEffect(() => { if (!focus) return // Kurz warten, damit ein gerade hinzugefügter Baustein in der Vorschau schon gerendert ist const timer = setTimeout(() => { frameRef.current?.contentWindow?.postMessage({ type: PREVIEW_FOCUS, id: focus.id }, window.location.origin) }, 120) return () => clearTimeout(timer) }, [focus]) useEffect(() => { function onMessage(event: MessageEvent) { if (event.origin === window.location.origin && event.source === frameRef.current?.contentWindow && event.data?.type === PREVIEW_READY) { post() } } window.addEventListener("message", onMessage) return () => window.removeEventListener("message", onMessage) }, []) useEffect(() => { const container = containerRef.current if (!container) return const observer = new ResizeObserver(([entry]) => setSize({ width: entry.contentRect.width, height: entry.contentRect.height })) observer.observe(container) return () => observer.disconnect() }, []) const deviceWidth = DEVICES[device].width const inset = device === "desktop" ? 0 : 16 const scale = size.width ? Math.min(1, (size.width - inset * 2) / deviceWidth) : 1 return (
{(Object.keys(DEVICES) as Device[]).map((key) => { const Icon = icons[key] return ( ) })}
{deviceWidth}px · {Math.round(scale * 100)}%
{size.width > 0 && (