@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
JavaScript
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 };