I currently have Highcharts implemented in a Chart
component in my application, but I need to make some changes to the Legend, went through most of the documentation, created some functions with Highcharts.wrap()
.
First, the Legend was simple, each legend item being
But now I need to change it into:
Here is what I got so far:
And with the click on the checkbox replicating the click on the Legend (symbol, label), which shows/hide the series line.
how? with this: (showing only the important parts)
const defaultOptions: Highcharts.Options = {
...,
legend: {
borderColor: "transparent",
verticalAlign: "top",
align: "left",
x: 14,
itemCheckboxStyle: {
cursor: "pointer",
border: "1px solid #62737a",
},
},
...,
plotOptions: {
series: {
...,
showCheckbox: true,
selected: true,
events: {
checkboxClick: function () {
this.setVisible(!this.visible);
},
},
},
...,
},
...,
}
If we only use showCheckbox: true
, the checkbox will be far on the right side of each label, not ideal. So this is needed: ( If possible I also would like tips on how to avoid the any error on TS in this case, without the comments ).
Highcharts.wrap(Highcharts.Legend.prototype, "positionCheckboxes", legendCheckboxPosition);
function legendCheckboxPosition(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
p: any,
scrollOffset: number
) {
const alignAttr = this.group.alignAttr;
const clipHeight = this.clipHeight || this.legendHeight;
let translateY: number;
if (alignAttr) {
translateY = alignAttr.translateY;
Highcharts.each(
this.allItems,
function (item: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
checkbox: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
legendItem: { getBBox: (arg0: boolean) => any };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
checkboxOffset: any;
}) {
const checkbox = item.checkbox;
const bBox = item.legendItem.getBBox(true);
let top;
if (checkbox) {
top = translateY + checkbox.y + (scrollOffset || 0) + 2;
Highcharts.css(checkbox, {
left:
alignAttr.translateX +
item.checkboxOffset +
checkbox.x -
100 -
bBox.width +
17 +
"px",
top: top + "px",
display: top > translateY - 6 && top < translateY + clipHeight - 6 ? "" : "none",
});
}
}
);
}
}
But with this done, I still need to make some changes, which are:
rtl
property inside the legends
options, which is supposed to change the order of Symbol and Label , but if I do that, it reverses, but it also reverse the order of the legends somehow, I'll show: -> Without rtl
: -> With rtl: true
inside the legends
options:
The checkbox distance I understand, because it will need to change my legendCheckboxPosition
function, my real problem here is the order of the legends being changed, like if I used legend.reversed: true
.. I found out that I can use the reversed
property to fix this, but I was wondering if this was a bug with something else..because in the documentation the rtl
property only changes the order of Symbol and Label , not the legends order.
:hover
of the checkbox, I tried using the legend.itemCheckboxStyle
but that doesn't allow me to add hover effects... (I need to place a box-shadow when hovering the checkbox)series.selected
property, and that I have the legendItemClick
event inside the plotOptions.series.events
, but inside that I don't have a this.setSelected
function, only this.setVisible
function. I tried using that, but it seems to freeze the chart, not doing anything. How to change the checkbox selection when clicking only in the legend item? Edit : Managed to solve this by adding this event to options.plotOptions.series.events
:
legendItemClick: function () {
const seriesIndex = this.index;
this.chart.series[seriesIndex].select();
},
Well.. that is my problem, with the hope that you guys can help me solve it.
A possible way to arranging the elements of legend would be to edit the legend in legend.labelFormatter
and add a Unicode line character. To have the checkbox on the right also you can style it in legend.itemCheckboxStyle
.
legend: {
symbolWidth: 0,
useHTML: true,
labelFormatter: function() {
return `<div>${this.name}<span style="color: green;">━</span></div>`;
},
itemDistance: 50,
itemCheckboxStyle: {
"width": "13px",
"height": "13px",
"margin-left": "-130px",
"position": "absolute"
}
},
API References:
https://api.highcharts.com/highcharts/legend.itemCheckboxStyle
After a lot of research and experimenting, I managed to have it all working.
Options (default options and events, also toggling visibility on load)
const series = GetSeries(opts, null);
...
// Check if Series is supposed to be hidden from load (from the checkbox selected prop), if it is, hide it
series.forEach((serie) => {
if (serie.selected !== undefined && serie.selected === false) serie.visible = false;
});
const defaultOptions: Highcharts.Options = {
...,
legend: {
borderColor: "transparent",
verticalAlign: "top",
align: "left",
x: 14,
},
...,
plotOptions: {
series: {
...
showCheckbox: true,
selected: true,
events: {
checkboxClick: function () {
this.setVisible(!this.visible);
},
legendItemClick: function () {
const seriesIndex = this.index;
this.chart.series[seriesIndex].select();
},
},
},
...
},
...
};
return mergeObjects(defaultOptions, opts);
}
And a lot of resolved extending Highcharts :
// Adjust position of the Highchart Legend Checkbox, and switch label and symbol in the legend
function legendPositionAdjustments(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
p: any,
scrollOffset: number
) {
const pixelsToREM = (value: number) => {
return value / 16;
};
const alignAttr = this.group.alignAttr;
const clipHeight = this.clipHeight || this.legendHeight;
let translateY: number;
const legendMainElement = this.box.parentGroup.element;
// Adjust the main legend element to be closer to the checkbox
if (legendMainElement) {
Highcharts.attr(legendMainElement, "transform", "translate(19, 10)");
}
if (alignAttr) {
translateY = alignAttr.translateY;
this.allItems.forEach(function (item: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
checkbox: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
legendItem: { getBBox: (arg0: boolean) => any };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
checkboxOffset: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
legendGroup: any;
}) {
const bBox = item.legendItem.getBBox(true);
// Change position of Label and Symbol in the Highcharts Legend
const legendItemElement = item.legendGroup.element;
const legendItemPath = legendItemElement.querySelector("path.highcharts-graph");
const legendItemPoint = legendItemElement.querySelector("path.highcharts-point");
const legendItemText = legendItemElement.querySelector("text");
if (legendItemPath) {
Highcharts.attr(legendItemPath, "transform", `translate(${bBox.width + 3}, 0)`);
}
if (legendItemPoint) {
Highcharts.attr(legendItemPoint, "transform", `translate(${bBox.width + 3}, 0)`);
}
if (legendItemText) {
Highcharts.attr(legendItemText, "x", 0);
}
// Adjust the position of the checkbox to the left side of the Highcharts Legend
const checkbox = item.checkbox;
let top;
let left;
if (checkbox) {
top = translateY + checkbox.y + (scrollOffset || 0) + 4;
left = alignAttr.translateX + item.checkboxOffset + checkbox.x - 100 - bBox.width + 17;
Highcharts.css(checkbox, {
left: pixelsToREM(left) + "rem",
top: pixelsToREM(top) + "rem",
display: top > translateY - 6 && top < translateY + clipHeight - 6 ? "" : "none",
});
}
});
}
}
// This function is called when triggering show/hide of series, always calling with visibility = true
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
function colorizeLegendRegardlessOfVisibility(this: any, proceed: any, item: any, visible: any) {
proceed.call(this, item, true);
}
This will:
Hope to help someone who encounters similar problems in the future.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.