mirror of
https://github.com/getredash/redash.git
synced 2026-03-22 10:00:17 -04:00
104 lines
3.9 KiB
JavaScript
104 lines
3.9 KiB
JavaScript
import { find, pick, reduce } from "lodash";
|
|
|
|
function fixLegendContainer(plotlyElement) {
|
|
const legend = plotlyElement.querySelector(".legend");
|
|
if (legend) {
|
|
let node = legend.parentNode;
|
|
while (node) {
|
|
if (node.tagName.toLowerCase() === "svg") {
|
|
node.style.overflow = "visible";
|
|
break;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
}
|
|
}
|
|
|
|
export default function applyLayoutFixes(plotlyElement, layout, updatePlot) {
|
|
// update layout size to plot container
|
|
layout.width = Math.floor(plotlyElement.offsetWidth);
|
|
layout.height = Math.floor(plotlyElement.offsetHeight);
|
|
|
|
const transformName = find(
|
|
["transform", "WebkitTransform", "MozTransform", "MsTransform", "OTransform"],
|
|
prop => prop in plotlyElement.style
|
|
);
|
|
|
|
if (layout.width <= 600) {
|
|
// Save current `layout.height` value because `updatePlot().then(...)` handler may be called multiple
|
|
// times within single update, and since the handler mutates `layout` object - it may lead to bugs
|
|
const layoutHeight = layout.height;
|
|
|
|
// change legend orientation to horizontal; plotly has a bug with this
|
|
// legend alignment - it does not preserve enough space under the plot;
|
|
// so we'll hack this: update plot (it will re-render legend), compute
|
|
// legend height, reduce plot size by legend height (but not less than
|
|
// half of plot container's height - legend will have max height equal to
|
|
// plot height), re-render plot again and offset legend to the space under
|
|
// the plot.
|
|
// Related issue: https://github.com/plotly/plotly.js/issues/1199
|
|
layout.legend = {
|
|
orientation: "h",
|
|
// locate legend inside of plot area - otherwise plotly will preserve
|
|
// some amount of space under the plot; also this will limit legend height
|
|
// to plot's height
|
|
y: 0,
|
|
x: 0,
|
|
xanchor: "left",
|
|
yanchor: "bottom",
|
|
};
|
|
|
|
// set `overflow: visible` to svg containing legend because later we will
|
|
// position legend outside of it
|
|
fixLegendContainer(plotlyElement);
|
|
|
|
updatePlot(plotlyElement, pick(layout, ["width", "height", "legend"])).then(() => {
|
|
const legend = plotlyElement.querySelector(".legend"); // eslint-disable-line no-shadow
|
|
if (legend) {
|
|
// compute real height of legend - items may be split into few columnns,
|
|
// also scrollbar may be shown
|
|
const bounds = reduce(
|
|
legend.querySelectorAll(".traces"),
|
|
(result, node) => {
|
|
const b = node.getBoundingClientRect();
|
|
result = result || b;
|
|
return {
|
|
top: Math.min(result.top, b.top),
|
|
bottom: Math.max(result.bottom, b.bottom),
|
|
};
|
|
},
|
|
null
|
|
);
|
|
// here we have two values:
|
|
// 1. height of plot container excluding height of legend items;
|
|
// it may be any value between 0 and plot container's height;
|
|
// 2. half of plot containers height. Legend cannot be larger than
|
|
// plot; if legend is too large, plotly will reduce it's height and
|
|
// show a scrollbar; in this case, height of plot === height of legend,
|
|
// so we can split container's height half by half between them.
|
|
layout.height = Math.floor(Math.max(layoutHeight / 2, layoutHeight - (bounds.bottom - bounds.top)));
|
|
// offset the legend
|
|
legend.style[transformName] = "translate(0, " + layout.height + "px)";
|
|
updatePlot(plotlyElement, pick(layout, ["height"]));
|
|
}
|
|
});
|
|
} else {
|
|
layout.legend = {
|
|
orientation: "v",
|
|
// vertical legend will be rendered properly, so just place it to the right
|
|
// side of plot
|
|
y: 1,
|
|
x: 1,
|
|
xanchor: "left",
|
|
yanchor: "top",
|
|
};
|
|
|
|
const legend = plotlyElement.querySelector(".legend");
|
|
if (legend) {
|
|
legend.style[transformName] = null;
|
|
}
|
|
|
|
updatePlot(plotlyElement, pick(layout, ["width", "height", "legend"]));
|
|
}
|
|
}
|