问题
I have managed to create a custom legend to display the series under each category in the legend. I am able to access the name, however I am not able to get the symbol to render along with the name in the legend. Please advice.
import React, { Component } from "react";
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 ReactDOM from "react-dom";
import { map, uniq, slice, filter, keyBy, uniqueId, at } from "lodash";
import { Menu, MenuDivider, MenuItem } from "@blueprintjs/core";
import { Row, Col } from "react-bootstrap";
import * as htmlToImage from "html-to-image";
import { saveAs } from "file-saver";
HC_more(Highcharts);
HC_exporting(Highcharts);
HC_series_label(Highcharts);
HC_boost(Highcharts);
const colors = [
"#800000",
"#9A6324",
"#808000",
"#469990",
"#000075",
"#e6194b",
"#f58231",
"#ffe119",
"#bfef45",
"#3cb44b",
"#42d4f4",
"#4363d8",
"#911eb4",
"#f032e6"
];
class Chart extends Component {
constructor(props) {
super(props);
const scenariosData = uniq(map(props.data.Series, "scenario"));
this.state = {
scenariosData
};
this.afterChart = this.afterChart.bind(this);
}
componentDidMount() {
const scenariosData = uniq(map(this.props.data.Series, "scenario"));
const mappedList = map(
uniq(map(this.props.data.Series, "scenario")),
(item) => {
return {
name: item,
ids: map(filter(this.props.data.Series, ["scenario", item]), (data) =>
data.id.toString()
)
};
}
);
this.setState(
{
scenariosData,
mappedList
},
() => {
if (this.state.scenariosData.length === 1) {
const legendAreaContainer = document.querySelector("#legendArea");
ReactDOM.unmountComponentAtNode(legendAreaContainer);
} else {
if (scenariosData.length > 1) {
const mappedNewList = keyBy(
this.internalChart.series,
"userOptions.id"
);
const groupedData = map(mappedList, ({ ids, ...item }) => ({
...item,
data: at(mappedNewList, ids)
}));
this.internalChart.series.forEach((item) => {
const menu = (
// <Row>
// {map(this.state.mappedList, (scenario) => {
// return (
// <Col key={uniqueId()}>
// <Menu className="text-center">
// <MenuDivider title={scenario.name} />
// {scenario.ids.map((i) => {
// return (
// <MenuItem
// key={uniqueId()}
// text={`${i} - ${
// this.props.data.Series.find(
// (j) => j.id === Number(i)
// ).segment
// }`}
// style={{
// opacity: item.opacity,
// color: colors[Number(i)]
// }}
// onClick={() => {
// alert(item.userOptions.id === Number(i));
// item.setVisible(!item.visible);
// item.opacity = item.visible ? 1 : 0.3;
// }}
// />
// );
// })}
// </Menu>
// </Col>
// );
// })}
// </Row>
<div className="row">
{map(groupedData, (scenario) => {
return (
<div key={uniqueId()} className="col">
<Menu className="text-center">
<MenuDivider title={scenario.name} />
{scenario.data.map((item) => {
return (
<MenuItem
key={uniqueId()}
text={
<div
style={{
backgroundColor: item.color,
opacity: item.visible ? 1 : 0.3
}}
>
<div id="symbol">{item.name}</div>
</div>
}
labelElement={item.userOptions.id}
onClick={() => {
item.setVisible(!item.visible);
}}
/>
);
})}
</Menu>
</div>
);
})}
</div>
);
ReactDOM.render(menu, document.getElementById("legendArea"));
});
}
}
}
);
}
componentDidUpdate(prevProps) {
if (prevProps.data !== this.props.data) {
const scenariosData = uniq(map(this.props.data.Series, "scenario"));
const mappedList = map(
uniq(map(this.props.data.Series, "scenario")),
(item) => {
return {
name: item,
ids: map(
filter(this.props.data.Series, ["scenario", item]),
(data) => data.id.toString()
)
};
}
);
this.setState(
{
scenariosData,
mappedList
},
() => {
if (this.state.scenariosData.length === 1) {
const legendAreaContainer = document.querySelector("#legendArea");
ReactDOM.unmountComponentAtNode(legendAreaContainer);
} else {
if (scenariosData.length > 1) {
const mappedNewList = keyBy(
this.internalChart.series,
"userOptions.id"
);
const groupedData = map(
this.state.mappedList,
({ ids, ...item }) => ({
...item,
data: at(mappedNewList, ids)
})
);
console.log("groupedData", groupedData);
this.internalChart.series.forEach((item) => {
const menu = (
<div className="row">
{map(groupedData, (scenario) => {
return (
<div key={uniqueId()} className="col">
<Menu className="text-center">
<MenuDivider title={scenario.name} />
{scenario.data.map((item) => {
return (
<MenuItem
key={uniqueId()}
text={
<div
style={{
backgroundColor: item.color,
opacity: item.visible ? 1 : 0.3
}}
>
<div id="symbol">{item.name}</div>
</div>
}
labelElement={item.userOptions.id}
onClick={() => {
item.setVisible(!item.visible);
}}
/>
);
})}
</Menu>
</div>
);
})}
</div>
);
ReactDOM.render(menu, document.getElementById("legendArea"));
});
}
}
}
);
}
}
afterChart(chart) {
this.internalChart = chart;
this.forceUpdate();
}
render() {
const options = {
chart: {
zoomType: "x",
resetZoomButton: {
position: {
align: "left", // by default
verticalAlign: "top", // by default
x: -10,
y: 10
}
},
type: "line",
height: this.props.height ? this.props.height : `60%`,
events: {}
},
exporting: {
menuItemDefinitions: {
// Custom definition
label: {
onclick: function () {
htmlToImage
.toBlob(document.getElementById("chartContainer"))
.then(function (blob) {
saveAs(blob, "my-node.png");
});
},
text: "Download png"
}
},
buttons: {
contextButton: {
menuItems: ["label"]
}
}
},
title: {
text: this.props.title
},
subtitle: {
text: ""
},
yAxis: {
title: {
text: null
},
labels: {
formatter: function () {
const self = this;
return Highcharts.numberFormat(self.value / 1, 0, ".");
},
style: {
fontSize: "13px"
}
}
},
legend:
this.state.scenariosData.length > 1
? {
enabled: false
}
: {
enabled: true,
itemStyle: {
fontSize: "15px"
}
},
credits: {
enabled: false
},
xAxis: {
categories: map(this.props.data.Dates, (item) => item.Date),
labels: {
style: {
fontSize: "13px"
}
}
},
plotOptions: {
series: {
boostThreshold: 2000,
label: {
enabled: false,
connectorAllowed: false
},
marker: {
enabled: false
}
}
},
tooltip: {
pointFormatter: function () {
return Highcharts.numberFormat(this.options.y / 1, 0, ".");
}
},
series: map(slice(this.props.data.Series, 0, 15), (item, index) => {
return {
name: this.state.scenariosData.length > 1 ? item.segment : item.name,
data: item.values,
type: this.props.chartType,
id: item.id.toString(),
color: colors[item.id - 1]
};
})
};
return (
<div id="chartContainer">
<HighchartsReact
highcharts={Highcharts}
options={options}
callback={this.afterChart}
{...this.props}
/>
<div id="legendArea" />
</div>
);
}
}
export default Chart;
Please advice.
This is my codesandbox link: https://codesandbox.io/s/intelligent-fog-cds4u?file=/src/Chart.jsx:0-12135
Expected Legend:
回答1:
I am not sure how the final layout should look like so I added just the symbol without any centering in your columns, please take a look:
https://codesandbox.io/s/bitter-currying-ibkdv
<div id="symbol">
{" "}
<div
style={{
backgroundColor: item.color,
width: "20px",
height: "20px",
float: "left",
borderRadius: "10px"
}}
></div>
{item.name}
</div>
来源:https://stackoverflow.com/questions/64811381/add-symbol-to-custom-legend-layout-highcharts-react