agentic-rag / frontend /src /components /PlotlyChart.tsx
vksepm
fixed plotly visualization and added evals
73af3c0
Raw
History Blame Contribute Delete
1.94 kB
/**
* Lazy-loaded Plotly chart.
* Plotly.js is ~3 MB; we import the minified dist to keep bundles lean.
*/
import { useEffect, useRef } from "react";
import type { PlotlySpec } from "../types";
interface Props {
spec: PlotlySpec;
}
export default function PlotlyChart({ spec }: Props) {
const divRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!divRef.current) return;
// Dynamic import keeps Plotly out of the initial bundle
import("plotly.js-dist-min").then((Plotly) => {
// Strip hardcoded width/height from AI-generated specs — they break
// responsive layout by overriding autosize. We control sizing via CSS.
const { width: _w, height: _h, ...restLayout } = spec.layout;
// Enforce minimum safe margins — use Math.max so spec can increase but
// not shrink below the floor. Zero margins on radial charts (sunburst,
// polar) clip outer-ring labels inside the SVG viewport.
const specMargin = (restLayout.margin ?? {}) as Record<string, number>;
const margin = {
l: Math.max(60, specMargin.l ?? 0),
r: Math.max(100, specMargin.r ?? 0),
t: Math.max(60, specMargin.t ?? 0),
b: Math.max(60, specMargin.b ?? 0),
};
Plotly.newPlot(
divRef.current!,
spec.data,
{
...restLayout,
autosize: true,
margin,
paper_bgcolor: "transparent",
plot_bgcolor: "#f8fafc",
font: { family: "Inter, sans-serif", size: 12 },
},
{ responsive: true, displayModeBar: "hover" }
);
});
return () => {
import("plotly.js-dist-min").then((Plotly) => {
if (divRef.current) Plotly.purge(divRef.current);
});
};
}, [spec]);
return (
<div className="mt-4 rounded-xl border border-slate-200 bg-white">
<div ref={divRef} style={{ width: "100%", height: 580 }} />
</div>
);
}