// PDF-Export-Dialog (modal). Fragt Massstab (1:N), Papierformat (A4/A3) und // Ausrichtung (Hoch/Quer) ab und löst den Vektor-PDF-Export aus. Muster wie // ImportDialog: abgedunkeltes Overlay, Klick auf den Hintergrund + Esc schließen. // Themedes Dropdown für die Auswahlfelder. // // Rein gesteuert: hält nur Formular-Zustand und meldet die Entscheidung über // `onExport` nach oben (App führt den eigentlichen Export aus, da es den Plan // erzeugt). Bezeichner englisch, UI-Text/Kommentare deutsch (CONVENTIONS.md). import { useEffect, useState } from "react"; import { t } from "../i18n"; import { Dropdown } from "./Dropdown"; import { SCALE_PRESETS } from "./TopBar"; import type { Orientation, PaperFormat } from "../export/exportPdf"; import { pageSizeMm } from "../export/exportPdf"; /** Entscheidung, die der Dialog beim Bestätigen liefert. */ export interface ExportPdfDecision { scaleDenominator: number; paper: PaperFormat; orientation: Orientation; } export interface ExportPdfDialogProps { /** Vorbelegung des Massstabs (aktueller Plan-Massstab). */ initialScale: number; /** Titel/Geschossname (nur Anzeige im Dialogkopf). */ planTitle: string; /** * Massstäbliche Inhalts-Maße des Plans in Metern (Breite/Höhe), für die * „passt aufs Blatt?"-Warnung. Optional. */ contentMeters?: { w: number; h: number }; onExport: (decision: ExportPdfDecision) => void; onClose: () => void; } export function ExportPdfDialog({ initialScale, planTitle, contentMeters, onExport, onClose, }: ExportPdfDialogProps) { useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [onClose]); const [scale, setScale] = useState(initialScale); const [paper, setPaper] = useState("a4"); const [orientation, setOrientation] = useState("landscape"); // Passt der Plan im gewählten Massstab aufs Blatt (abzüglich Rand)? Best-effort- // Hinweis; der Export läuft trotzdem (Plan ragt dann über den Blattrand). const fits = (() => { if (!contentMeters) return true; const { w: pageW, h: pageH } = pageSizeMm(paper, orientation); const margin = 12; // Rand + etwas Luft fürs Schriftfeld const k = 1000 / scale; // mm je Meter return ( contentMeters.w * k <= pageW - margin * 2 && contentMeters.h * k <= pageH - margin * 2 ); })(); const onConfirm = () => onExport({ scaleDenominator: scale, paper, orientation }); const scaleOptions = SCALE_PRESETS.map((n) => ({ value: String(n), label: `1:${n}`, })); return (
e.stopPropagation()} >
{t("export.title")}
{t("export.plan")} {planTitle}
{t("export.scale")}
setScale(Number(v))} title={t("export.scale")} triggerClassName="tb-dd-mono" options={scaleOptions} />
{t("export.paper")}
setPaper(v as PaperFormat)} title={t("export.paper")} options={[ { value: "a4", label: "A4" }, { value: "a3", label: "A3" }, ]} />
{t("export.orientation")}
setOrientation(v as Orientation)} title={t("export.orientation")} options={[ { value: "portrait", label: t("export.orientation.portrait") }, { value: "landscape", label: t("export.orientation.landscape") }, ]} />
{!fits &&
{t("export.warnFit")}
}
); }