mirror of
https://github.com/RGJorge/ContainerFlow.git
synced 2026-08-03 07:21:42 +02:00
v0.1.5 — export graph as PNG (closes #4)
Adds a download button to the bottom-left React Flow controls that exports the current canvas view as a PNG with the dot grid preserved. - New util src/client/utils/exportPng.ts: html-to-image capture + manual canvas composition (solid bg + dot grid + captured layer). Works around html-to-image's SVG <pattern> limitations and produces a deterministic 3x retina PNG. - During capture, temporary CSS disables ring-shadow halos and forces service node bodies to a solid dark fill so the dot grid doesn't bleed through. State is still indicated by border colors and the inner state dot. Group node headers are excluded so their outlines stay intact. - New component src/client/components/ExportPngButton.tsx integrated as a custom ControlButton inside <Controls> (bottom-left). - App.tsx: id="dashboard-canvas" + data-no-export on project filter. - i18n: 4 keys (EN + ES) for the button and 3 error states. - New dependency: html-to-image@1.11.13
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import { useState } from "react";
|
||||
import { ControlButton } from "@xyflow/react";
|
||||
import { Download, Loader2 } from "lucide-react";
|
||||
import { useT } from "../i18n";
|
||||
import { exportGraphAsPng, downloadPng } from "../utils/exportPng";
|
||||
|
||||
interface Props {
|
||||
onError?: (message: string) => void;
|
||||
}
|
||||
|
||||
export function ExportPngButton({ onError }: Props) {
|
||||
const { t } = useT();
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const handleClick = async () => {
|
||||
if (busy) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
const { dataUrl } = await exportGraphAsPng();
|
||||
downloadPng(dataUrl, window.location.hostname);
|
||||
} catch (err) {
|
||||
const code = err instanceof Error ? err.message : "UNKNOWN";
|
||||
const message =
|
||||
code === "EMPTY_GRAPH"
|
||||
? t("controls.exportPng.empty")
|
||||
: code === "GRAPH_TOO_LARGE"
|
||||
? t("controls.exportPng.tooLarge")
|
||||
: t("controls.exportPng.failed");
|
||||
onError?.(message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ControlButton onClick={handleClick} title={t("controls.exportPng")} disabled={busy}>
|
||||
{busy ? <Loader2 className="animate-spin" /> : <Download />}
|
||||
</ControlButton>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user