How to download React Layout as PDF

78 Views Asked by At

I use react-grid-layout library in my project. When the user clicks on the chart he wants to add, the addChart function runs and I update the layoutInfo state.I create all the charts in layoutData using the renderChart function and show them to the user on the layout.When the user wants to add a new blank page, the addNewLayout function runs and a new layout is added. What I want to do now is that when the user presses the Download button, he can download all the layouts he created to his computer as pdf. All charts added on these layouts should also be visible on the downloaded pdf page. I want to do this with the saveAsLayout function, how can I do it? Here is my code;

import RGL, { WidthProvider } from "react-grid-layout";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";
const ReactGridLayout = WidthProvider(RGL);
const NewGraph = () => {
  const [layoutInfo, setLayoutInfo] = useState({
    layouts: [
      {
        layout: [],
        layoutData: [],
        history: [[]],
        redoStack: [[]],
      },
    ],
    reportTitle: "Report Title",
  });
  const [visibleLayoutIndex, setVisibleLayoutIndex] = useState(0);
  const ChartTypes = [{ name: "Line", icon: "line-chart.png" }];
  function addChart(chartType) {
    const visibleLayout = layoutInfo.layouts[visibleLayoutIndex];
    const newChartId = visibleLayout.layoutData.length + 1;
    const newChartName = `NewChart_${newChartId}_$(visibleLayoutIndex)`;
    const newChartTitle = `${chartType} Chart`;

    const newChart = {
      chartName: newChartName,
      chartTitle: newChartTitle,
      chartType: chartType,
      data: [
        {
          datasetX: ["X - Axis"],
          datasetY: ["Y - Axis"],
          xAxisData: [[0, 0, 0, 0, 0]],
          yAxisData: [[0, 0, 0, 0, 0]],
        },
      ],
      yAxis: [],
      xAxis: [],
    };
    const updatedLayoutInfo = { ...layoutInfo };

    updatedLayoutInfo.layouts[visibleLayoutIndex].layout = [
      ...updatedLayoutInfo.layouts[visibleLayoutIndex].layout,
      { i: newChartName, x: 0, y: 0, w: 2, h: 2 },
    ];
    updatedLayoutInfo.layouts[visibleLayoutIndex].layoutData = [
      ...updatedLayoutInfo.layouts[visibleLayoutIndex].layoutData,
      newChart,
    ];
    setLayoutInfo(updatedLayoutInfo);
  }
  const renderChart = (chartName, chartType, chartTitle, data) => {
    switch (chartType) {
    case 'Line':
        {
          const datasets = data[0].yAxisData.map((itemData, index) => ({
            label: `${data[0].datasetY[index]}`,
            data: itemData,
          }));

          const datasetsX = data[0].xAxisData[0];

          const xAxisLabels = data[0].datasetX[0];

          const series = datasets.map((data) => {
            return {
              name: data.label,
              data: data.data,
            };
          });
          const options = {
            chart: {
              height: 350,
              type: 'line',
              zoom: {
                enabled: false,
              },
              toolbar: {
                show: false,
              },
            },
            colors: ['#36a2eb', '#ff9f40', '#4bc0c0', '#ff6384', '#c9cbcf', '#9966ff', '#ffcd56'],
            dataLabels: {
              enabled: true,
            },
            stroke: {
              curve: 'straight',
            },
            title: {
              text: chartTitle,
              align: 'left',
            },
            grid: {
              row: {
                colors: ['#f3f3f3', 'transparent'], // takes an array which will be repeated on columns
                opacity: 0.5,
              },
            },

            yaxis: {
              title: {
                text: datasets[0].label,
              },
            },

            xaxis: {
              categories: datasetsX,
              title: {
                text: xAxisLabels,
              },
            },
          };
          return (
            <div key={chartName} id="chart-container">
              <div id={`chart-${chartName}`} className="chart">
                <ReactApexChart className={isPreviewOpen ? 'preview-chart' : ''} options={options} series={series} type="line" height={350} width={500} />
              </div>
              <div id={`chart-data-${chartName}`} className="chart-data" style={{ display: 'none' }}>
                {/* Hidden div to store chart data */}
                {JSON.stringify(data)} {/* Store your chart data here */}
              </div>
            </div>
          );
        }
    }
  };
  const addNewLayout = () => {
    const newLayout = {
      layout: [],
      layoutData: [],
      history: [[]],
      redoStack: [[]],
    };

    setLayoutInfo((prevLayoutData) => ({
      ...prevLayoutData,
      layouts: [...prevLayoutData.layouts, newLayout],
    }));
    handleLayoutChange(layoutInfo.layouts.length);
  };
  const saveAsLayout = async () => {
    
  };


  return (
    <div>
      <CNavbar expand="lg" colorScheme="light">
        <CContainer fluid>
          <CCollapse className="navbar-collapse" visible={visible}>
            <CNavbarNav className="ml-auto">
              <CNavItem id="CNavItem">
                {ChartTypes.map((chartType) => (
                  <CNavLink
                    key={chartType.name}
                    onClick={() => addChart(chartType.name)}
                    className="cursor"
                  >
                    <img
                      src={`./img/${chartType.icon}`}
                      alt={`${chartType.name} Chart`}
                      title={`${chartType.name} Chart`}
                      className="chart-img cursor"
                    ></img>
                  </CNavLink>
                ))}
              </CNavItem>

              <CNavItem>
                <CNavLink className="cursor" onClick={addNewLayout}>
                  <img
                    src="./img/new-document.png"
                    alt="New"
                    title="New"
                    className="navbar-sidebar-icon"
                  ></img>
                </CNavLink>
              </CNavItem>

              <CNavItem id="CNavItem">
                <CNavLink id="downloadButton" onClick={() => saveAsLayout()}>
                  <img
                    src="./img/download.png"
                    alt="Download"
                    title="Download"
                    className="navbar-sidebar-icon"
                  ></img>
                </CNavLink>
              </CNavItem>
            </CNavbarNav>
          </CCollapse>
        </CContainer>
      </CNavbar>

      <div className="content">
        <div className="RGL">
          <ReactGridLayout
            id="preview-layout"
            layout={layoutInfo.layouts[visibleLayoutIndex].layout}
            isDraggable={false}
            isResizable={false}
            className="layout preview-layout"
            cols={3}
            rowHeight={180}
          >
            {layoutInfo.layouts[visibleLayoutIndex].layoutData.map(
              (reportData) => (
                <div key={reportData.chartName}>
                  {renderChart(
                    reportData.chartName,
                    reportData.chartType,
                    reportData.chartTitle,
                    reportData.data
                  )}
                </div>
              )
            )}
          </ReactGridLayout>
        </div>
      </div>
    </div>
  );
};  

I tried this ;

const saveAsLayout = async () => {
    const pdf = new jsPDF('p', 'mm', 'a4');

    for (let i = 0; i < layoutInfo.layouts.length; i++) {
      const layout = layoutInfo.layouts[i];

      // Her bir layout için ayrı bir div oluşturun ve bu div içine layout'ı render edin
      const div = document.createElement('div');
      div.className = 'layout'; // Bu stil isminin layout olduğunu varsayalım
      document.body.appendChild(div); // Body içine ekle (veya uygun bir yerde)

      ReactDOM.render(
        <ReactGridLayout layout={layout.layout} isDraggable={false} isResizable={false} className="layout" cols={3} rowHeight={180}>
          {layout.layoutData.map((reportData) => (
            <div key={reportData.chartName}>{renderChart(reportData.chartName, reportData.chartType, reportData.chartTitle, reportData.data)}</div>
          ))}
        </ReactGridLayout>,
        div,
      );

      // Oluşturduğumuz layout içeriğini yakalayalım
      const canvas = await html2canvas(div, { scale: 2, useCORS: true });

      // Her bir canvas görüntüsünü PDF'e ekle
      if (i !== 0) {
        pdf.addPage(); // Yeni bir PDF sayfası ekle
      }
      const imgData = canvas.toDataURL('image/png');
      const width = pdf.internal.pageSize.getWidth();
      const height = pdf.internal.pageSize.getHeight();
      pdf.addImage(imgData, 'PNG', 0, 0, width, height);

      // Oluşturduğumuz layout div'ini kaldıralım
      document.body.removeChild(div);
    }

    // Oluşturulan PDF'i indir
    pdf.save('grid_layouts.pdf');
  };


I use the html2canvas library to convert to PDF. You can capture the charts visually, but their contents do not appear. The reason for this is that when I convert the chart content to an image, I only get its image. What changes should I make in saveAsLayout?

0

There are 0 best solutions below