Reputation: 2675
I want to create a chart where the legend is split into number of columns based on distinct scenario
from the JSON. The legend items should be categorized under each header, but from what I did, the header always gets prepended to each legend item. Please advice.
This is what I have:
import React from "react";
import "./styles.css";
import * as Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
import HC_more from "highcharts/highcharts-more";
import HC_exporting from "highcharts/modules/exporting";
import HC_series_label from "highcharts/modules/series-label";
import HC_boost from "highcharts/modules/boost";
import { map, slice } from "lodash";
HC_more(Highcharts);
HC_exporting(Highcharts);
HC_series_label(Highcharts);
HC_boost(Highcharts);
const App = (props) => {
const options = {
colors: [
"#800000",
"#9A6324",
"#808000",
"#469990",
"#000075",
"#e6194b",
"#f58231",
"#ffe119",
"#bfef45",
"#3cb44b",
"#42d4f4",
"#4363d8",
"#911eb4",
"#f032e6"
],
chart: {
zoomType: "x",
resetZoomButton: {
position: {
align: "left", // by default
verticalAlign: "top", // by default
x: -10,
y: 10
}
},
type: "line",
height: props.height ? props.height : `60%`
},
exporting: {
enabled: true,
chartOptions: {
xAxis: [
{
max: null
}
]
}
},
title: {
text: props.title
},
subtitle: {
text: ""
},
yAxis: {
title: {
text: null
},
labels: {
formatter: function () {
return `${Highcharts.numberFormat(
this.value / 1,
props.decimalPlaces,
"."
)}`;
},
style: {
fontSize: "13px"
}
}
},
legend: {
itemStyle: {
fontSize: "15px"
},
floating: false,
itemMarginBottom: 5,
width: 180,
itemWidth: 100,
useHTML: true,
labelFormatter: function () {
console.log(this);
console.log(`data.Series[Number(this.userOptions.id)]`);
return `<div>${
data.Series[Number(this.userOptions.id) - 1].scenario
}<div>${this.name}</div></div>`;
}
},
credits: {
enabled: false
},
xAxis: {
categories: data.Dates.map((item) => item.Date),
labels: {
style: {
fontSize: "13px"
}
}
},
plotOptions: {
series: {
boostThreshold: 2000,
label: {
enabled: false,
connectorAllowed: false
}
}
},
tooltip: {
pointFormatter: function () {
return `${Highcharts.numberFormat(
this.options.y / 1,
props.decimalPlaces,
"."
)}`;
}
},
series: map(slice(data.Series, 0, 15), (item) => {
return {
name: item.segment,
data: item.values,
type: "line",
id: item.id.toString()
};
})
};
return (
<HighchartsReact highcharts={Highcharts} options={options} {...props} />
);
};
export default App;
Stackblitz Link: https://codesandbox.io/s/fragrant-cherry-2qhr6?file=/src/App.js:0-25119 https://stackblitz.com/edit/react-7owjmq
Upvotes: 0
Views: 1303
Reputation: 11633
After digging more into your requirement I decided that it will be better to position the already existed legend items and create the custom labels name than trying to render them in the custom legend. I added some comments in the code, so I think that everything is clearly explained - in case of any doubts feel free to ask.
Simple JS demo: https://jsfiddle.net/BlackLabel/8wnq2y4k/
React version: https://stackblitz.com/edit/react-djeehw
events: {
render() {
const chart = this;
const labelsPos = 450;
let firstColumnY = 50; // Distance from the chart to the legend
let secondColumnY = 50; // Distance from the chart to the legend
chart.legend.allItems.forEach(l => {
// First column
if (l.userOptions.scenario === 'Class 1') {
let group = l.legendGroup;
group.translate(-chart.plotWidth / 2 + chart.plotLeft + group.getBBox().width, firstColumnY);
firstColumnY += 23; // Where 23 is a height of the one legend label
}
// Second column
if (l.userOptions.scenario === 'Class 2') {
let group = l.legendGroup;
group.translate(chart.plotWidth / 2, secondColumnY);
secondColumnY += 23; // Where 23 is a height of the one legend label
}
});
// Keep labels position responsive by deleting them and render again
if (chart.firstLabel && chart.secondLabel) {
chart.firstLabel.destroy();
chart.secondLabel.destroy();
}
chart.firstLabel = chart.renderer.label('Class 1', 0, labelsPos).css({
'font-size': '22px'
})
.add();
chart.secondLabel = chart.renderer.label('Class 2', chart.plotWidth, labelsPos).css({
'font-size': '22px'
})
.add();
chart.secondLabel.translate(chart.secondLabel.x - chart.secondLabel.getBBox().width + 10, labelsPos)
}
}
API: https://api.highcharts.com/highcharts/chart.events.render
API: https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#label
API: https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#destroy
API: https://api.highcharts.com/class-reference/Highcharts.SVGElement#translate
If you want to try to implement the symbols to your custom legend here is a part of the code responsible for it: https://github.com/highcharts/highcharts/blob/master/js/Core/Legend.js#L479
Upvotes: 2