diff --git a/package.json b/package.json index ed573845c..f151e4e6b 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "@ollion/flow-system-icon": "latest", "@ollion/flow-table": "workspace:*", "d3": "^7.6.1", + "html2canvas": "^1.4.1", "jspdf": "^2.5.1", "lit": "^3.1.0" }, diff --git a/packages/flow-dashboard/src/components/f-dashboard/f-dashboard.ts b/packages/flow-dashboard/src/components/f-dashboard/f-dashboard.ts index dbe44bcfe..59fa5bb7f 100644 --- a/packages/flow-dashboard/src/components/f-dashboard/f-dashboard.ts +++ b/packages/flow-dashboard/src/components/f-dashboard/f-dashboard.ts @@ -50,7 +50,7 @@ export class FDashboard extends FRoot { render() { return html`
- ${this.config.widgets.map(wgt => { + ${this.config?.widgets.map(wgt => { return keyed( wgt.id, html`
= createRef(); chartLegends: Ref = createRef(); @@ -252,7 +252,7 @@ export class FTimeseriesChart extends FRoot { height="100%" > ${svg``} + >${svg``} ) => this.handleHeaderInput(event, columnHeader[1])} @@ -297,7 +296,7 @@ export class FTableSchema extends FRoot { .actions=${actions} .align=${cell.align} data-background="${this.stickyCellBackground}" - ?sticky-left=${ifDefined(sticky)} + ?sticky-left=${sticky} >${this.getCellTemplate(row.data[columnHeader[0]], highlightTerm)} `; })} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 73c2d5c77..bd1c581e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: d3: specifier: ^7.6.1 version: 7.8.5 + html2canvas: + specifier: ^1.4.1 + version: 1.4.1 jspdf: specifier: ^2.5.1 version: 2.5.1 @@ -6883,7 +6886,6 @@ packages: engines: {node: '>= 0.6.0'} requiresBuild: true dev: false - optional: true /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -7713,7 +7715,6 @@ packages: dependencies: utrie: 1.0.2 dev: false - optional: true /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -9705,7 +9706,6 @@ packages: css-line-break: 2.1.0 text-segmentation: 1.0.3 dev: false - optional: true /http-assert@1.5.0: resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} @@ -13830,7 +13830,6 @@ packages: dependencies: utrie: 1.0.2 dev: false - optional: true /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -14418,7 +14417,6 @@ packages: dependencies: base64-arraybuffer: 1.0.2 dev: false - optional: true /uuid@3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} diff --git a/stories/flow-dashboard/f-dashboard.stories.ts b/stories/flow-dashboard/f-dashboard.stories.ts index 657ae5dbf..bf64c2cfe 100644 --- a/stories/flow-dashboard/f-dashboard.stories.ts +++ b/stories/flow-dashboard/f-dashboard.stories.ts @@ -1,14 +1,11 @@ import { Meta } from "@storybook/web-components"; import { html } from "lit-html"; -import { - FDashboard, - FDashboardConfig, - FDashboardWidget, - FTimeseriesChartConfig -} from "@ollion/flow-dashboard"; +import type { FDashboard, FDashboardConfig, FDashboardWidget } from "@ollion/flow-dashboard"; import { generateTimeseriesChartData } from "./mock-data-utils"; import { faker } from "@faker-js/faker"; import { createRef, ref } from "lit/directives/ref.js"; +import html2canvas from "html2canvas"; +import jsPDF from "jspdf"; export default { title: "@ollion/flow-dashboard/f-dashboard", @@ -32,7 +29,7 @@ const getWidgets = () => { ]; const widgets: FDashboardWidget[] = []; const startFrom = new Date(); - for (let index = 0; index < 10; index++) { + for (let index = 0; index < 20; index++) { if (index % 2 === 0) { widgets.push({ type: "timeseries", @@ -52,7 +49,9 @@ const getWidgets = () => { > - ${name} + ${name} ${description} `; @@ -101,8 +100,10 @@ const getWidgets = () => { return widgets; }; + const Template = () => { const dashboardRef = createRef(); + const imgRef = createRef(); const dashboardConfig: FDashboardConfig = { widgets: getWidgets() }; @@ -114,7 +115,68 @@ const Template = () => { } }; - return html` + /** + * Download file as image in pdf + * + * + */ + const downloadFile = () => { + const element = document.querySelector("#dashboard-to-export") as FDashboard; + + html2canvas(element, { scale: 1 }).then(function (canvas) { + const imgData = canvas.toDataURL("image/png"); + const imgWidth = 794; + const pageHeight = 1115; + const imgHeight = (canvas.height * imgWidth) / canvas.width; + let heightLeft = imgHeight; + // Initialize jsPDF + const pdf = new jsPDF({ + orientation: "p", + unit: "px", + format: [794, 1115] + }); + pdf.setDisplayMode("original"); + let position = 0; // give some top padding to first page + + const allAnchors = element.querySelectorAll("a"); + for (let l = 0; l < allAnchors.length; l++) { + const anchorElement = allAnchors.item(l); + const linkX = anchorElement.getBoundingClientRect().x - element.getBoundingClientRect().x; + const linkY = anchorElement.getBoundingClientRect().y - element.getBoundingClientRect().y; + const linkWidth = anchorElement.offsetWidth; + const linkHeight = anchorElement.offsetHeight; + + pdf.link(linkX, linkY, linkWidth, linkHeight, { + url: anchorElement.href + }); + } + + pdf.addImage(imgData, "PNG", 0, position, imgWidth, imgHeight); + heightLeft -= pageHeight; + + while (heightLeft >= 0) { + position += heightLeft - imgHeight; // top padding for other pages + pdf.addPage(); + + pdf.addImage(imgData, "PNG", 0, position, imgWidth, imgHeight); + heightLeft -= pageHeight; + } + + // Save the PDF + pdf.save("canvas_to_pdf.pdf"); + }); + }; + + return html` + Ollion + { gap="auto" align="middle-left" > - Click on randomize button to generate new data - + Click on export button to generate pdf + - + + `; };