Jacobina / static /js /plot.js
marinarosa's picture
initial commit
be82719
Raw
History Blame Contribute Delete
2.47 kB
// Themed Plotly rendering. Figures come from the backend as plain
// {data, layout} JSON; we overlay transparent backgrounds and the current
// theme's font/grid colors, and re-render live plots on theme switch.
//
// IMPORTANT (inherited from miru-tracer): never give a rendered figure a
// fixed width inside a scrollable container — Plotly's responsive resize +
// a toggling scrollbar forms a ResizeObserver feedback loop that freezes
// the browser. Figures stay autosized; wide data pans via the figure's own
// dragmode.
const live = new Map(); // element -> figure JSON
function themeColors() {
const styles = getComputedStyle(document.documentElement);
return {
text: styles.getPropertyValue('--text').trim() || '#e6e9f0',
muted: styles.getPropertyValue('--text-muted').trim() || '#8b93a7',
grid: styles.getPropertyValue('--plot-grid').trim() || 'rgba(127,127,127,0.18)',
font: styles.getPropertyValue('--font-ui').trim() || 'Inter, sans-serif',
};
}
function themedLayout(layout = {}) {
const c = themeColors();
const themed = structuredClone(layout);
themed.paper_bgcolor = 'rgba(0,0,0,0)';
themed.plot_bgcolor = 'rgba(0,0,0,0)';
themed.font = { ...(themed.font || {}), color: c.text, family: c.font };
themed.autosize = true;
delete themed.width; // never fixed-width (see module note)
for (const key of Object.keys(themed)) {
if (key.startsWith('xaxis') || key.startsWith('yaxis')) {
themed[key] = {
...themed[key],
gridcolor: c.grid, zerolinecolor: c.grid, linecolor: c.grid,
tickfont: { ...(themed[key]?.tickfont || {}), color: c.muted },
};
}
}
if (themed.legend) themed.legend = { ...themed.legend, font: { color: c.muted } };
return themed;
}
export function renderPlot(target, fig) {
if (!fig || !fig.data) { clearPlot(target); return; }
live.set(target, fig);
window.Plotly.react(target, fig.data, themedLayout(fig.layout), {
responsive: true,
displaylogo: false,
modeBarButtonsToRemove: ['select2d', 'lasso2d'],
});
target.classList.add('has-plot');
}
export function clearPlot(target) {
live.delete(target);
if (target.classList.contains('has-plot')) {
window.Plotly.purge(target);
target.classList.remove('has-plot');
}
target.replaceChildren();
}
export function rethemePlots() {
for (const [target, fig] of live) {
if (target.isConnected) renderPlot(target, fig);
else live.delete(target);
}
}