| import { lazy, Suspense } from "react"; | |
| import type { Data, Layout } from "plotly.js"; | |
| const LazyPlot = lazy(async () => { | |
| const [{ default: Plotly }, { default: createPlotlyComponent }] = await Promise.all([ | |
| // @ts-expect-error plotly.js-dist-min ships no type declarations; runtime shape matches plotly.js | |
| import("plotly.js-dist-min"), | |
| import("react-plotly.js/factory"), | |
| ]); | |
| return { default: createPlotlyComponent(Plotly) }; | |
| }); | |
| export interface PlotlyChartSpec { | |
| schema?: string; | |
| chart_type?: string; | |
| title?: string; | |
| plotly: { | |
| data: Data[]; | |
| layout?: Partial<Layout>; | |
| }; | |
| } | |
| export function PlotlyChartBlock({ spec }: { spec: PlotlyChartSpec }) { | |
| if (!spec?.plotly?.data) return null; | |
| return ( | |
| <div className="my-3 max-w-full overflow-x-auto rounded-lg border border-slate-200 bg-white p-2"> | |
| <Suspense | |
| fallback={ | |
| <div className="flex h-64 items-center justify-center text-xs text-slate-400">Loading chart…</div> | |
| } | |
| > | |
| <LazyPlot | |
| data={spec.plotly.data} | |
| layout={{ autosize: true, ...spec.plotly.layout }} | |
| config={{ displaylogo: false, responsive: true }} | |
| style={{ width: "100%", height: "360px" }} | |
| useResizeHandler | |
| /> | |
| </Suspense> | |
| </div> | |
| ); | |
| } | |