向自定义图例布局添加符号-Highcharts React

问题描述

我设法创建了一个自定义图例,以在图例的每个类别下显示系列。我可以访问名称,但是无法在图例中获得与名称一起呈现的符号。请指教。

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,(item) => {
          return {
            name: item,ids: map(
              filter(this.props.data.Series,(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,...item }) => ({
                  ...item,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,".");
          },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,".");
        }
      },series: map(slice(this.props.data.Series,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;

请咨询。

这是我的codeandBox链接https://codesandbox.io/s/intelligent-fog-cds4u?file=/src/Chart.jsx:0-12135

预期图例:

enter image description here

解决方法

我不确定最终的布局应该是什么样子,所以我只添加了符号,而没有在列中居中,请看一下:

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>