UNPKG

@dschz/solid-plotly

Version:

SolidJS wrapper for Plotly.js – reactive and performant charts powered by Plotly, built for Solid.

175 lines (171 loc) 4.61 kB
// src/createPlotComponent.tsx import { tryCatch } from "@dschz/try-catch"; import { createEffect, createSignal, mergeProps, onCleanup, onMount, splitProps } from "solid-js"; // src/constants.ts var PLOT_COMPONENT_EVENT_NAMES = [ "onAfterExport", "onAfterPlot", "onAnimated", "onAnimatingFrame", "onAnimationInterrupted", "onAutoSize", "onBeforeExport", "onBeforePlot", "onClick", "onClickAnnotation", "onDeselect", "onDoubleClick", "onEvent", "onFramework", "onHover", "onLegendClick", "onLegendDoubleClick", "onRedraw", "onRelayout", "onRelayouting", "onRestyle", "onSelected", "onSelecting", "onSliderChange", "onSliderEnd", "onSliderStart", "onSunburstClick", "onTransitioning", "onTransitionInterrupted", "onUnhover" ]; // src/utils.ts var convertComponentEventNameToPlotlyEventName = (eventName) => { const eventNameWithoutOnPrefix = eventName.substring(2); return `plotly_${eventNameWithoutOnPrefix.toLowerCase()}`; }; var attachComponentEvents = (plotElement, componentEventMap) => { for (const eventName in componentEventMap) { const eventHandler = componentEventMap[eventName]; if (!eventHandler) continue; const plotlyEventName = convertComponentEventNameToPlotlyEventName( eventName ); plotElement.on(plotlyEventName, eventHandler); } }; var removeComponentEvents = (plotElement, componentEventMap) => { for (const eventName in componentEventMap) { const componentEventName = eventName; const eventHandler = componentEventMap[componentEventName]; if (!eventHandler) continue; const plotlyEventName = convertComponentEventNameToPlotlyEventName(componentEventName); plotElement.removeAllListeners(plotlyEventName); } }; var getFigureData = (plotElement) => { const pElem = plotElement; const figureData = { data: pElem.data, layout: pElem.layout, frames: pElem._transitionData?._frames || [] }; return figureData; }; // src/createPlotComponent.tsx var createPlotComponent = (Plotly) => (props) => { const _props = mergeProps( { id: "solid-plotly", config: {}, data: [], frames: [], layout: {}, useResize: true, style: { position: "relative", display: "inline-block" } }, props ); let container; let plotElement; const [initialized, setInitialized] = createSignal(false); const [containerProps] = splitProps(_props, ["id", "class", "style", "ref"]); const [eventProps] = splitProps(_props, PLOT_COMPONENT_EVENT_NAMES); const [plotProps] = splitProps(_props, [ "data", "layout", "config", "onInitialized", "onPurge", "onUpdate", "onError", "onResize", "useResize" ]); const layout = () => plotProps.useResize ? { ...plotProps.layout, autosize: true } : plotProps.layout; const config = () => plotProps.useResize ? { ...plotProps.config, responsive: true } : plotProps.config; onMount(async () => { const [error, element] = await tryCatch( Plotly.newPlot(container, plotProps.data, layout(), config()) ); if (error) { plotProps.onError?.(error); return; } Plotly.Plots.resize(element); attachComponentEvents(element, eventProps); plotProps.onInitialized?.(getFigureData(element), element); plotElement = element; setInitialized(true); }); createEffect(() => { if (!initialized()) return; const updatePlot = async () => { const [error, element] = await tryCatch( Plotly.react(container, plotProps.data, layout(), config()) ); if (error) { plotProps.onError?.(error); return; } plotProps.onUpdate?.(getFigureData(element), element); }; void updatePlot(); }); createEffect(() => { if (!initialized() || !plotProps.useResize) return; const resizeHandler = () => { Plotly.Plots.resize(plotElement); plotProps.onResize?.(); }; const resizeObserver = new ResizeObserver(resizeHandler); resizeObserver.observe(container); onCleanup(() => { resizeObserver.disconnect(); }); }); onCleanup(() => { if (!initialized()) return; removeComponentEvents(plotElement, eventProps); Plotly.purge(plotElement); plotProps.onPurge?.(getFigureData(plotElement), plotElement); setInitialized(false); }); return <div id={containerProps.id} class={containerProps.class} style={containerProps.style} ref={(el) => { container = el; containerProps.ref?.(el); }} />; }; export { createPlotComponent };