Resize highcharts using react-grid-layout not working

后端 未结 2 516
再見小時候
再見小時候 2021-02-06 15:14

I am working in react and using highcharts with react-grid-layout to resize the elements in div. Somehow resizable feature is working for images but not with highchart. Grid.js

相关标签:
2条回答
  • 2021-02-06 15:20

    Here is the sample solution to make it fit in the Grid....

    import React from 'react';
    import './App.css';
    import '/node_modules/react-grid-layout/css/styles.css';
    import '/node_modules/react-resizable/css/styles.css';
    import GridLayout from 'react-grid-layout';
    import Highcharts from "highcharts/highstock";
    import HighchartsReact from "highcharts-react-official";
    
    const options = {
      series: [
        {
          data: [1, 2, 3]
        }
      ]
    };
    
    class MyFirstGrid extends React.Component {
      constructor(props) {
        super(props);
        this.myRef = React.createRef();
        this.conRef = React.createRef();
      }
    
      render() {
        // layout is an array of objects, see the demo for more complete usage
        var layout = [
          { i: "a", x: 0, y: 0, w: 5, h: 5 },
          { i: "b", x: 1, y: 0, w: 3, h: 2 },
          { i: "c", x: 4, y: 0, w: 1, h: 2 }
        ];
        return (
          <GridLayout
            className="layout"
            layout={layout}
            cols={12}
            rowHeight={30}
            width={1200}
            onResizeStop={function(event) {
    
             this.myRef.current.chart.setSize(this.conRef.current.clientWidth,this.conRef.current.clientHeight)
              console.log('hello', event);
            }.bind(this)}
          >
            <div ref={this.conRef}  style={{ backgroundColor: "#00000000" }} key="a">
              <HighchartsReact
                ref= {this.myRef}
                containerProps={{ style: { width: '100%', height: '100%' } }}
                options={options}
                highcharts={Highcharts}
              />
            </div>
    
            <div style={{ backgroundColor: "red" }} key="b">
              b
            </div>
            <div style={{ backgroundColor: "blue" }} key="c">
              c
            </div>
          </GridLayout>
        );
      }
    }
    
    
    export default MyFirstGrid;
    
    0 讨论(0)
  • 2021-02-06 15:34

    So, I was struggling with the same issue using highcharts-react-official and react-grid-layout.

    Here is how I finally got it working.


    tl;dr

    1. Give height 100% to all of your chart's parents up to the grid item.
    2. There is an annoying div that highcharts creates by himself. Find a way to identify it and give it height 100%.
    3. Give height 100% to the chart itself.
    4. Use the react highcharts callback to get your chart object.
    5. When your component updates reflow your chart.

    Below is my responsive grid layout, just to give some context.

    // Component/Grid/Grid.js
    <ResponsiveGridLayout
        ...
    >
        {this.state.widgets.map((widget) =>
            <Card key={widget.DWG_ID}>
                <Widget
                    widget={widget}
                />
            </Card>
        )}
    </ResponsiveGridLayout>
    

    Now, inside the Widget Component, set the height of any div that will be a parent of your highchart to 100%.

    // Component/Widget/Widget.js
    <CardBody className="widgetContent">
        <CardTitle className="widget-title">{this.props.widget.DWG_LABEL}</CardTitle>
        <Chart      
            widget={this.props.widget}
        />}     
    </CardBody>   
    

    For the jsx above I only needed to do this for the element CardBody with class widgetContent, so

    // Component/Widget/Widget.css
    .widgetContent { height: 100%; }
    

    Now, in the chart component (where all the fun was), I had to create a very ugly div just to be able to identify the outer-most div that highcharts creates.

    elements created by highcharts

    The infamous div in question can be seen in the image above, right under the div with class highchartsWrapper, with the property data-highcharts-chart . This div was the only parent of my chart that I could not identify directly to give 100% height. So I created the wrapper to be able to identify it unequivocally. Note that in the chart options we passed a class name as well, to be able to give the chart itself the css height property.

    If anybody has a neater idea of how to identify this problematic div please let me know.

    // Component/Chart/Chart.js
    options = {
        ...
        chart: { className: 'chart' }
    }
    <div className="highchartsWrapper">
        <HighchartsReact
            highcharts={Highcharts}
            options={options}
            callback={(chart) => this.setChart(chart)}
        />
    </div>
    

    So I could give it the css

    // Component/Chart/Chart.css
    .highchartsWrapper > div {
        height: 100%;
    }
    .chart {
        height: 100%;
    }
    

    Now your highchart would ideally assume the correct width and height. But there was another complication: when the highchart renders for the first time and checks his parent's height, react-grid-layout isn't yet done with his resizing magic. This means your chart will be teeny-tiny. Moreover, when you resize your grid items you want your highchart to resize to its new parent size. But wait, I've worked with highcharts before, I know how to do this! The good old chart.reflow() ! Unfortunately this ended up not being that easy.

    To start with, just getting the chart object on which I can call reflow wasn't very straightforward. If you notice, I gave my HighchartsReact a callback

    (chart) => this.setChart(chart)
    

    This is just to store the chart object as a property of my class component. My setChart function does only the following:

    setChart(chart) {
        this.chart = chart;
    }
    

    It might seem stupid to do it like this. Why not just give setChart directly to the callback property of HighchartsReact? Well because if you do, as per the highcharts-react-official documentation, your this inside the setChart function would be your chart object... All very confusing, but it seems to work like this.

    Again, if somebody has a neater solution, let me know

    Finally, we can call our this.chart.reflow() when our Chart Component is updated. what I did was something like

    constructor() {
        super(props)
    
        this.firstResize = true;
    }
    
    componentDidUpdate(prevProps) {
    
        if (this.didWidgetSizeChange(prevProps) || this.isFirstResize) {
            this.chart.reflow();
            this.isFirstResize = false;
        }
    }
    

    When the component updates we call the chart reflow. On the componentDidMount the grid item doesn't have his final size yet, so I used a flag to figure out the first update (that would be exactly that: grid item has finished first resize). Then for any other update I wrote a function that basically compares the previous layout for this grid item with the new to decide if the size or width have changed. If so, we reflow again to resize the highcharts to the new grid item size.

    Hope this helps!

    Peace!

    0 讨论(0)
提交回复
热议问题