diff --git a/bricks/basic/docs/eo-snapshot.md b/bricks/basic/docs/eo-snapshot.md new file mode 100644 index 000000000..66f00f96f --- /dev/null +++ b/bricks/basic/docs/eo-snapshot.md @@ -0,0 +1,51 @@ +构件 `eo-snapshot` + +## Examples + +### Basic + +```yaml preview +- brick: eo-snapshot + properties: + id: snapshot + events: + save: + action: console.log + upload: + action: console.log +- brick: eo-button + properties: + textContent: Save As Image + style: + margin-right: 10px + events: + click: + - action: console.log + - target: "#snapshot" + method: saveAsImage + args: + - name: image +- brick: eo-button + properties: + textContent: Save As PDF + style: + margin-right: 10px + events: + click: + - action: console.log + - target: "#snapshot" + method: saveAsPDF + args: + - name: pdf +- brick: eo-button + properties: + textContent: Capture And Upload + events: + click: + - action: console.log + - target: "#snapshot" + method: captureImageAndUpload + args: + - name: upload + bucketName: examples +``` diff --git a/bricks/basic/package.json b/bricks/basic/package.json index 9cc5ad032..6969fc836 100644 --- a/bricks/basic/package.json +++ b/bricks/basic/package.json @@ -48,6 +48,8 @@ "@next-core/types": "^1.7.1", "@next-shared/general": "^0.7.2", "classnames": "^2.3.2", + "html2canvas": "^1.4.1", + "jspdf": "^2.5.1", "lodash": "^4.17.21", "react": "0.0.0-experimental-ee8509801-20230117" }, diff --git a/bricks/basic/src/bootstrap.ts b/bricks/basic/src/bootstrap.ts index ed9bcbe65..a471e4af9 100644 --- a/bricks/basic/src/bootstrap.ts +++ b/bricks/basic/src/bootstrap.ts @@ -35,3 +35,4 @@ import "./v3-widget-mate/index.js"; import "./easyops-avatar/index.js"; import "./data-providers/lock-body-scroll/lock-body-scroll.js"; import "./data-providers/set-theme-by-apps.js"; +import "./snapshot/index.js"; diff --git a/bricks/basic/src/snapshot/i18n.ts b/bricks/basic/src/snapshot/i18n.ts new file mode 100644 index 000000000..df15ecdeb --- /dev/null +++ b/bricks/basic/src/snapshot/i18n.ts @@ -0,0 +1,18 @@ +export enum K {} +// HELLO = "HELLO", + +const en: Locale = { + // HELLO: "Hello", +}; + +const zh: Locale = { + // HELLO: "你好", +}; + +export const NS = "bricks/basic/eo-snapshot"; + +export const locales = { en, zh }; + +type Locale = { [k in K]: string } & { + [k in K as `${k}_plural`]?: string; +}; diff --git a/bricks/basic/src/snapshot/index.spec.tsx b/bricks/basic/src/snapshot/index.spec.tsx new file mode 100644 index 000000000..bb1390347 --- /dev/null +++ b/bricks/basic/src/snapshot/index.spec.tsx @@ -0,0 +1,24 @@ +import { describe, test, expect, jest } from "@jest/globals"; +import { act } from "react-dom/test-utils"; +import "./"; +import { EoSnapshot } from "./index.js"; + +jest.mock("@next-core/theme", () => ({})); + +describe("eo-snapshot", () => { + test("basic usage", async () => { + const element = document.createElement("eo-snapshot") as EoSnapshot; + + expect(element.shadowRoot).toBeFalsy(); + + act(() => { + document.body.appendChild(element); + }); + expect(element.shadowRoot?.childNodes.length).toBeGreaterThan(0); + + act(() => { + document.body.removeChild(element); + }); + expect(element.shadowRoot?.childNodes.length).toBe(0); + }); +}); diff --git a/bricks/basic/src/snapshot/index.tsx b/bricks/basic/src/snapshot/index.tsx new file mode 100644 index 000000000..ceab19b05 --- /dev/null +++ b/bricks/basic/src/snapshot/index.tsx @@ -0,0 +1,103 @@ +// istanbul ignore file: nothing logical except calling html2canvas and jspdf. +import React from "react"; +import { EventEmitter, createDecorators } from "@next-core/element"; +import { ReactNextElement } from "@next-core/react-element"; +import "@next-core/theme"; +import styleText from "./styles.shadow.css"; +import { + buildImageUrl, + capture, + downloadImage, + getCanvasBlob, + uploadFile, +} from "./snapshot.js"; + +const { defineElement, event, method } = createDecorators(); + +export type SnapshotConf = { + id?: "string"; + name: string; +}; + +/** + * 截图或者截图上传构件 + */ +export +@defineElement("eo-snapshot", { + styleTexts: [styleText], +}) +class EoSnapshot extends ReactNextElement { + /* + * 保存图片或者PDF时触发 + */ + @event({ type: "save" }) + accessor #onSave!: EventEmitter<{ + fileType: "image" | "pdf"; + status: "saving" | "succeed" | "failed"; + }>; + + /* + * 上传图片时触发 + */ + @event({ type: "upload" }) + accessor #onUpload!: EventEmitter<{ + url?: string; + status: "uploading" | "succeed" | "failed"; + }>; + + /* + * 截图并保存为图片 + */ + @method() + async saveAsImage(conf: SnapshotConf) { + try { + this.#onSave.emit({ fileType: "image", status: "saving" }); + const canvas = await capture(); + await downloadImage(canvas, conf.name); + this.#onSave.emit({ fileType: "image", status: "succeed" }); + } catch (e) { + this.#onSave.emit({ fileType: "image", status: "failed" }); + } + } + + /* + * 截图并保存为PDF + */ + @method() + async saveAsPDF(conf: SnapshotConf) { + try { + const { jsPDF } = await import("jspdf"); + this.#onSave.emit({ fileType: "pdf", status: "saving" }); + const canvas = await capture(conf.id); + const imageData = canvas.toDataURL("image/jpeg", 1.0); + const pdf = new jsPDF("l", "pt", [canvas.width, canvas.height]); + pdf.addImage(imageData, "JPEG", 0, 0, canvas.width, canvas.height); + await pdf.save(`${conf.name}.pdf`, { returnPromise: true }); + this.#onSave.emit({ fileType: "pdf", status: "succeed" }); + } catch (e) { + this.#onSave.emit({ fileType: "pdf", status: "failed" }); + } + } + + /* + * 截图并上传 + */ + @method() + async captureImageAndUpload(conf: SnapshotConf & { bucketName: string }) { + try { + this.#onUpload.emit({ status: "uploading" }); + const canvas = await capture(conf.id); + const blob = await getCanvasBlob(canvas); + const files = new File([blob], `${conf.name}.png`, { type: blob.type }); + const result = await uploadFile(files, conf.bucketName); + const url = buildImageUrl(conf.bucketName, result.data.objectName); + this.#onUpload.emit({ url, status: "succeed" }); + } catch (e) { + this.#onUpload.emit({ status: "failed" }); + } + } + + render() { + return null; + } +} diff --git a/bricks/basic/src/snapshot/snapshot.ts b/bricks/basic/src/snapshot/snapshot.ts new file mode 100644 index 000000000..fc0a92a84 --- /dev/null +++ b/bricks/basic/src/snapshot/snapshot.ts @@ -0,0 +1,91 @@ +// istanbul ignore file: nothing logical except calling html2canvas. +import { http } from "@next-core/http"; +import { getBasePath } from "@next-core/runtime"; +import _html2canvas from "html2canvas"; + +// There is a type issue like i18next +const html2canvas = _html2canvas as unknown as typeof _html2canvas.default; +export type UploadStatus = "uploading" | "done" | "error"; + +export function capture(id?: string): Promise { + const target = document.querySelector(`#${id}`) || document.body; + + return new Promise(function (resolve, reject) { + html2canvas(document.body, { + logging: false, + scale: window.devicePixelRatio < 3 ? window.devicePixelRatio : 2, + width: target.clientWidth || window.innerWidth, + height: target.clientHeight || window.innerHeight, + foreignObjectRendering: true, + allowTaint: true, + useCORS: true, + removeContainer: false, + }) + .then(function (canvas) { + resolve(canvas); + }) + .catch(reject); + }); +} + +export function downloadImage( + canvas: HTMLCanvasElement, + name = "image" +): Promise { + return new Promise((resolve, reject) => { + try { + const url = canvas.toDataURL("image/png"); + + const image = new Image(); + image.onload = function () { + const canvas = document.createElement("canvas"); + canvas.width = image.width; + canvas.height = image.height; + const ctx = canvas.getContext("2d"); + ctx?.drawImage(image, 0, 0, image.width, image.height); + + const url = canvas.toDataURL("image/png"); + const a = document.createElement("a"); + a.href = url; + a.download = name; + const event = new MouseEvent("click"); + a.dispatchEvent(event); + }; + image.src = url; + resolve("succeed"); + } catch (e) { + reject("failed"); + } + }); +} + +export function getCanvasBlob(canvas: HTMLCanvasElement): Promise { + return new Promise(function (resolve, reject) { + canvas.toBlob(function (blob: Blob | null) { + if (blob) { + resolve(blob); + } else { + reject(); + } + }); + }); +} + +export function uploadFile( + file: File, + bucketName: string +): Promise<{ data: { objectName: string } }> { + const formData = new FormData(); + formData.append("file", file, file.name); + + const url = `/next/api/gateway/object_store.object_store.PutObject/api/v1/objectStore/bucket/${bucketName}/object`; + + return http.request(url, { + method: "PUT", + body: formData, + }); +} + +export function buildImageUrl(bucketName: string, objectName: string) { + return `${getBasePath()}api/gateway/logic.object_store_service/api/v1/objectStore/bucket/${bucketName}/object/${objectName}`; +} diff --git a/bricks/basic/src/snapshot/styles.shadow.css b/bricks/basic/src/snapshot/styles.shadow.css new file mode 100644 index 000000000..654c39f9e --- /dev/null +++ b/bricks/basic/src/snapshot/styles.shadow.css @@ -0,0 +1,7 @@ +:host { + display: inline-block; +} + +:host([hidden]) { + display: none; +} diff --git a/bricks/data-view/docs/indicator-card.md b/bricks/data-view/docs/indicator-card.md new file mode 100644 index 000000000..f15cd2fc7 --- /dev/null +++ b/bricks/data-view/docs/indicator-card.md @@ -0,0 +1,75 @@ +构件 `data-view.indicator-card` + +## Examples + +### Column + +```yaml preview +- brick: data-view.indicator-card + properties: + layout: "column" + datasource: + - value: 300 + desc: 月碳排放量 + unit: (吨) + - value: 1000.33 + desc: 季度碳排放总量 + unit: (吨) + - value: 2507.55 + desc: 年度碳排放总量 + unit: (吨) +``` + +### Column Townhouse + +```yaml preview +- brick: data-view.indicator-card + properties: + layout: "column-townhouse" + datasource: + - value: 300 + desc: 月碳排放量 + unit: (吨) + - value: 1000.33 + desc: 季度碳排放总量 + unit: (吨) + - value: 2507.55 + desc: 年度碳排放总量 + unit: (吨) +``` + +### Row + +```yaml preview +- brick: data-view.indicator-card + properties: + layout: "row" + datasource: + - value: 300 + desc: 月碳排放量 + unit: (吨) + - value: 1000.33 + desc: 季度碳排放总量 + unit: (吨) + - value: 2507.55 + desc: 年度碳排放总量 + unit: (吨) +``` + +### Row Townhouse + +```yaml preview +- brick: data-view.indicator-card + properties: + layout: "row-townhouse" + datasource: + - value: 300 + desc: 月碳排放量 + unit: (吨) + - value: 1000.33 + desc: 季度碳排放总量 + unit: (吨) + - value: 2507.55 + desc: 年度碳排放总量 + unit: (吨) +``` diff --git a/bricks/data-view/src/asset/images/block.png b/bricks/data-view/src/asset/images/block.png new file mode 100644 index 000000000..6e3bc457b Binary files /dev/null and b/bricks/data-view/src/asset/images/block.png differ diff --git a/bricks/data-view/src/asset/images/colBg.png b/bricks/data-view/src/asset/images/colBg.png new file mode 100644 index 000000000..7b0f36b1c Binary files /dev/null and b/bricks/data-view/src/asset/images/colBg.png differ diff --git a/bricks/data-view/src/asset/images/divider.png b/bricks/data-view/src/asset/images/divider.png new file mode 100644 index 000000000..54f80bef3 Binary files /dev/null and b/bricks/data-view/src/asset/images/divider.png differ diff --git a/bricks/data-view/src/asset/images/rowBg.png b/bricks/data-view/src/asset/images/rowBg.png new file mode 100644 index 000000000..6f88b97b9 Binary files /dev/null and b/bricks/data-view/src/asset/images/rowBg.png differ diff --git a/bricks/data-view/src/bootstrap.ts b/bricks/data-view/src/bootstrap.ts index 4cfc79d44..bada1fcb6 100644 --- a/bricks/data-view/src/bootstrap.ts +++ b/bricks/data-view/src/bootstrap.ts @@ -36,3 +36,4 @@ import "./basic-index-group/index.js"; import "./data-display-flipper/index.js"; import "./data-display-flipper-fifth/index.js"; import "./data-display-flipper-sixth/index.js"; +import "./indicator-card/index.js"; diff --git a/bricks/data-view/src/indicator-card/i18n.ts b/bricks/data-view/src/indicator-card/i18n.ts new file mode 100644 index 000000000..b0b56b592 --- /dev/null +++ b/bricks/data-view/src/indicator-card/i18n.ts @@ -0,0 +1,18 @@ +export enum K {} +// HELLO = "HELLO", + +const en: Locale = { + // HELLO: "Hello", +}; + +const zh: Locale = { + // HELLO: "你好", +}; + +export const NS = "bricks/data-view/indicator-card"; + +export const locales = { en, zh }; + +type Locale = { [k in K]: string } & { + [k in K as `${k}_plural`]?: string; +}; diff --git a/bricks/data-view/src/indicator-card/index.spec.tsx b/bricks/data-view/src/indicator-card/index.spec.tsx new file mode 100644 index 000000000..01942d2ca --- /dev/null +++ b/bricks/data-view/src/indicator-card/index.spec.tsx @@ -0,0 +1,127 @@ +import { describe, test, expect, jest } from "@jest/globals"; +import { act } from "react-dom/test-utils"; +import "./"; +import { IndicatorCard } from "./index.js"; + +jest.mock("@next-core/theme", () => ({})); + +describe("data-view.indicator-card", () => { + test("basic usage", async () => { + const element = document.createElement( + "data-view.indicator-card" + ) as IndicatorCard; + + expect(element.shadowRoot).toBeFalsy(); + + act(() => { + document.body.appendChild(element); + }); + expect(element.shadowRoot?.childNodes.length).toBeGreaterThan(1); + + act(() => { + document.body.removeChild(element); + }); + expect(element.shadowRoot?.childNodes.length).toBe(0); + }); + + test('layout="column"', async () => { + const element = document.createElement( + "data-view.indicator-card" + ) as IndicatorCard; + element.datasource = [ + { + value: 300, + desc: "月碳排放量", + unit: "(吨)", + }, + { + value: 1000.33, + desc: "季度碳排放总量", + unit: "(吨)", + }, + ]; + act(() => { + document.body.appendChild(element); + }); + const container = element.shadowRoot?.querySelector(".container"); + expect(container.querySelectorAll(".col-wrapper")).toHaveLength(2); + expect(element.shadowRoot?.querySelector(".townhouse")).toBeNull(); + }); + + test('layout="column-townhouse"', async () => { + const element = document.createElement( + "data-view.indicator-card" + ) as IndicatorCard; + element.datasource = [ + { + value: 300, + desc: "月碳排放量", + unit: "(吨)", + }, + { + value: 1000.33, + desc: "季度碳排放总量", + unit: "(吨)", + }, + ]; + element.layout = "column-townhouse"; + act(() => { + document.body.appendChild(element); + }); + const container = element.shadowRoot?.querySelector(".container"); + expect(container.querySelectorAll(".col-wrapper")).toHaveLength(2); + expect(element.shadowRoot?.querySelector(".townhouse")).toBeTruthy(); + }); + + test('layout="row"', async () => { + const element = document.createElement( + "data-view.indicator-card" + ) as IndicatorCard; + element.datasource = [ + { + value: 300, + desc: "月碳排放量", + unit: "(吨)", + }, + { + value: 1000.33, + desc: "季度碳排放总量", + unit: "(吨)", + }, + ]; + element.layout = "row"; + act(() => { + document.body.appendChild(element); + }); + const container = element.shadowRoot?.querySelector(".container"); + expect(container.querySelectorAll(".row-wrapper")).toHaveLength(2); + expect(container.querySelectorAll(".row-left img")).toHaveLength(2); + expect(element.shadowRoot?.querySelector(".townhouse")).toBeNull(); + }); + + test('layout="row-townhouse"', async () => { + const element = document.createElement( + "data-view.indicator-card" + ) as IndicatorCard; + element.datasource = [ + { + value: 300, + desc: "月碳排放量", + unit: "(吨)", + }, + { + value: 1000.33, + desc: "季度碳排放总量", + unit: "(吨)", + }, + ]; + element.layout = "row-townhouse"; + act(() => { + document.body.appendChild(element); + }); + const container = element.shadowRoot?.querySelector(".container"); + expect(container.querySelectorAll(".row-wrapper")).toHaveLength(2); + expect(container.querySelectorAll(".row-left img")).toHaveLength(2); + expect(element.shadowRoot?.querySelector(".townhouse")).toBeTruthy(); + }); +}); diff --git a/bricks/data-view/src/indicator-card/index.tsx b/bricks/data-view/src/indicator-card/index.tsx new file mode 100644 index 000000000..f2da2c6a8 --- /dev/null +++ b/bricks/data-view/src/indicator-card/index.tsx @@ -0,0 +1,90 @@ +import React, { useMemo } from "react"; +import { createDecorators } from "@next-core/element"; +import { ReactNextElement } from "@next-core/react-element"; +import "@next-core/theme"; +import styleText from "./styles.shadow.css"; +import classnames from "classnames"; +import blockUrl from "../asset/images/block.png"; + +const { defineElement, property } = createDecorators(); + +/** + * 构件 `data-view.indicator-card` + */ +export +@defineElement("data-view.indicator-card", { + styleTexts: [styleText], +}) +class IndicatorCard extends ReactNextElement { + /** + * 展示类型, `column`类型为上下三行、`row`类型为左右两行模式 + * @default `column` + */ + @property({ attribute: false }) + accessor layout: Layout = "column"; + + /** + * 数据源 + * @default `[]` + */ + @property({ attribute: false }) + accessor datasource: Datasource[] = []; + + render() { + return ( + + ); + } +} + +export type Layout = "column" | "column-townhouse" | "row" | "row-townhouse"; +export interface Datasource { + value: any; + desc: string; + unit?: string; +} +export interface IndicatorCardComponentProps { + datasource: Datasource[]; + layout?: Layout; +} +export function IndicatorCardComponent({ + datasource, + layout, +}: IndicatorCardComponentProps) { + const renderColumnView = useMemo(() => { + return datasource.map((data, index) => ( +
+
{data.value}
+
{data.desc}
+
{data.unit}
+
+ )); + }, [datasource]); + + const renderRowView = useMemo(() => { + return datasource.map((data, index) => ( +
+
+ +
+
+
{data.desc}
+
{data.value}
+
+
+ )); + }, [datasource]); + + return ( +
+ {layout.includes("row") ? renderRowView : renderColumnView} +
+ ); +} diff --git a/bricks/data-view/src/indicator-card/styles.shadow.css b/bricks/data-view/src/indicator-card/styles.shadow.css new file mode 100644 index 000000000..237531522 --- /dev/null +++ b/bricks/data-view/src/indicator-card/styles.shadow.css @@ -0,0 +1,98 @@ +:host { + display: inline-block; +} + +:host([hidden]) { + display: none; +} +.container { + gap: 8px; +} +.flex { + display: flex; +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} +.col-wrapper { + padding: 9px 21px 7px; + text-align: center; + background: url("../asset/images/rowBg.png") center bottom repeat-x + rgba(28, 58, 117, 0.2); + border-top: 1px solid; + border-image: radial-gradient(circle, rgb(0, 149, 255), rgba(0, 149, 255, 0)) + 2 / 1 / 0 stretch; +} + +.townhouse { + gap: 0; + .col-wrapper { + background: none; + padding: 9px 25px 7px; + &:not(:last-child) { + border-right: 1px solid; + border-image: url("../asset/images/divider.png") 2 / 1 / 0 stretch; + background: none; + } + &:last-child { + border: none; + } + } + .row-wrapper { + background: none; + padding: 10px 18px; + } +} +.col-value { + font-size: 20px; + font-weight: bold; + color: #0bf9fe; + line-height: 24px; +} +.col-desc { + font-size: 12px; + font-weight: 400; + color: rgba(255, 255, 255, 1); + line-height: 18px; +} +.col-unit { + font-size: 10px; + font-weight: 400; + color: rgba(255, 255, 255, 0.65); + line-height: 14px; +} +.row-left { + & img { + width: 100%; + position: relative; + top: -7px; + } +} +.row-right { + text-align: left; +} +.row-wrapper { + padding: 10px 14px; + gap: 11px; + background: url("../asset/images/colBg.png") center bottom repeat-x + rgba(28, 58, 117, 0.2); +} +.row-desc { + font-size: 14px; + font-weight: 500; + color: #ffffff; + line-height: 9px; + color: rgba(255, 255, 255, 0.8); +} +.row-value { + font-size: 24px; + font-weight: 900; + color: #ffffff; + line-height: 28px; + padding-top: 8px; +} diff --git a/shared/common-bricks/common-bricks.json b/shared/common-bricks/common-bricks.json index 6e9b1ccb2..95808b16a 100644 --- a/shared/common-bricks/common-bricks.json +++ b/shared/common-bricks/common-bricks.json @@ -28,7 +28,8 @@ "eo-app-bar-wrapper", "eo-page-title", "eo-counter-badge", - "eo-easyops-avatar" + "eo-easyops-avatar", + "eo-snapshot" ], "icons": [ "eo-antd-icon", diff --git a/yarn.lock b/yarn.lock index 10919d5e8..3b7913cc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1111,6 +1111,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.14.0": + version "7.23.9" + resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" + integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/standalone@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.22.9.tgz#8ac034a30867dc50ff5a7904d771832edcd7a192" @@ -3451,6 +3458,11 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/raf@^3.4.0": + version "3.4.3" + resolved "https://registry.npmmirror.com/@types/raf/-/raf-3.4.3.tgz#85f1d1d17569b28b8db45e16e996407a56b0ab04" + integrity sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw== + "@types/react-dom@^18.0.0", "@types/react-dom@^18.0.11": version "18.0.11" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.11.tgz#321351c1459bc9ca3d216aefc8a167beec334e33" @@ -4267,6 +4279,11 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.npmmirror.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + autoprefixer@^10.4.16: version "10.4.16" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" @@ -4775,6 +4792,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.npmmirror.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -4950,6 +4972,20 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001550.tgz#6ec6a2239eb2a8123cc26cfe0571db5c79eb8669" integrity sha512-p82WjBYIypO0ukTsd/FG3Xxs+4tFeaY9pfT4amQL8KWtYH7H9nYwReGAbMTJ0hsmRO8IfDtsS6p3ZWj8+1c2RQ== +canvg@^3.0.6: + version "3.0.10" + resolved "https://registry.npmmirror.com/canvg/-/canvg-3.0.10.tgz#8e52a2d088b6ffa23ac78970b2a9eebfae0ef4b3" + integrity sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q== + dependencies: + "@babel/runtime" "^7.12.5" + "@types/raf" "^3.4.0" + core-js "^3.8.3" + raf "^3.4.1" + regenerator-runtime "^0.13.7" + rgbcolor "^1.0.1" + stackblur-canvas "^2.0.0" + svg-pathdata "^6.0.3" + capital-case@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" @@ -5603,6 +5639,11 @@ core-js-compat@^3.31.0, core-js-compat@^3.32.2: dependencies: browserslist "^4.22.1" +core-js@^3.6.0, core-js@^3.8.3: + version "3.35.1" + resolved "https://registry.npmmirror.com/core-js/-/core-js-3.35.1.tgz#9c28f8b7ccee482796f8590cc8d15739eaaf980c" + integrity sha512-IgdsbxNyMskrTFxa9lWHyMwAJU5gXOPP+1yO+K59d50VLVAIDAbs7gIv705KzALModfK3ZrSZTPNpC0PQgIZuw== + core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -6306,6 +6347,11 @@ domhandler@^5.0.1, domhandler@^5.0.2: dependencies: domelementtype "^2.3.0" +dompurify@^2.2.0: + version "2.4.7" + resolved "https://registry.npmmirror.com/dompurify/-/dompurify-2.4.7.tgz#277adeb40a2c84be2d42a8bcd45f582bfa4d0cfc" + integrity sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ== + domutils@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" @@ -7011,6 +7057,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fflate@^0.4.8: + version "0.4.8" + resolved "https://registry.npmmirror.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae" + integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== + fflate@~0.6.9: version "0.6.10" resolved "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz#5f40f9659205936a2d18abf88b2e7781662b6d43" @@ -7765,9 +7816,9 @@ html-parse-stringify@^3.0.1: dependencies: void-elements "3.1.0" -html2canvas@^1.4.1: +html2canvas@^1.0.0-rc.5, html2canvas@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + resolved "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== dependencies: css-line-break "^2.1.0" @@ -9112,6 +9163,21 @@ jsonparse@^1.2.0, jsonparse@^1.3.1: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== +jspdf@^2.5.1: + version "2.5.1" + resolved "https://registry.npmmirror.com/jspdf/-/jspdf-2.5.1.tgz#00c85250abf5447a05f3b32ab9935ab4a56592cc" + integrity sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA== + dependencies: + "@babel/runtime" "^7.14.0" + atob "^2.1.2" + btoa "^1.2.1" + fflate "^0.4.8" + optionalDependencies: + canvg "^3.0.6" + core-js "^3.6.0" + dompurify "^2.2.0" + html2canvas "^1.0.0-rc.5" + jsprim@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" @@ -12336,6 +12402,13 @@ quick-lru@^6.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-6.1.1.tgz#f8e5bf9010376c126c80c1a62827a526c0e60adf" integrity sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q== +raf@^3.4.1: + version "3.4.1" + resolved "https://registry.npmmirror.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -13000,6 +13073,11 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== +regenerator-runtime@^0.13.7: + version "0.13.11" + resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + regenerator-runtime@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" @@ -13199,6 +13277,11 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== +rgbcolor@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/rgbcolor/-/rgbcolor-1.0.1.tgz#d6505ecdb304a6595da26fa4b43307306775945d" + integrity sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw== + rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -13657,6 +13740,11 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stackblur-canvas@^2.0.0: + version "2.6.0" + resolved "https://registry.npmmirror.com/stackblur-canvas/-/stackblur-canvas-2.6.0.tgz#7876bab4ea99bfc97b69ce662614d7a1afb2d71b" + integrity sha512-8S1aIA+UoF6erJYnglGPug6MaHYGo1Ot7h5fuXx4fUPvcvQfcdw2o/ppCse63+eZf8PPidSu4v1JnmEVtEDnpg== + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -13932,6 +14020,11 @@ svg-parser@^2.0.4: resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== +svg-pathdata@^6.0.3: + version "6.0.3" + resolved "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz#80b0e0283b652ccbafb69ad4f8f73e8d3fbf2cac" + integrity sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw== + svgo@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.0.2.tgz#5e99eeea42c68ee0dc46aa16da093838c262fe0a"