UNPKG

@dschz/solid-plotly

Version:

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

169 lines (163 loc) 4.97 kB
import { template, use, effect, setAttribute, className, style } from 'solid-js/web'; import { tryCatch } from '@dschz/try-catch'; import { mergeProps, createSignal, splitProps, onMount, createEffect, onCleanup } from 'solid-js'; // src/createPlotComponent.tsx // 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 _tmpl$ = /* @__PURE__ */ template(`<div>`); 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 (() => { var _el$ = _tmpl$(); use((el) => { container = el; containerProps.ref?.(el); }, _el$); effect((_p$) => { var _v$ = containerProps.id, _v$2 = containerProps.class, _v$3 = containerProps.style; _v$ !== _p$.e && setAttribute(_el$, "id", _p$.e = _v$); _v$2 !== _p$.t && className(_el$, _p$.t = _v$2); _p$.a = style(_el$, _v$3, _p$.a); return _p$; }, { e: void 0, t: void 0, a: void 0 }); return _el$; })(); }; export { createPlotComponent };