From 5288865a5222cb8ed40ac45f39b98b410b4a288b Mon Sep 17 00:00:00 2001 From: alexchen Date: Wed, 25 Oct 2023 18:11:22 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat():=20=E6=95=B0=E6=8D=AE=E6=8C=87?= =?UTF-8?q?=E6=A0=87=E7=BB=84-4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes DASHBOARD-485 --- bricks/data-view/docs/indicator-card.md | 75 +++++++++++ bricks/data-view/src/asset/images/block.png | Bin 0 -> 720 bytes bricks/data-view/src/asset/images/colBg.png | Bin 0 -> 816 bytes bricks/data-view/src/asset/images/divider.png | Bin 0 -> 326 bytes bricks/data-view/src/asset/images/rowBg.png | Bin 0 -> 8140 bytes bricks/data-view/src/bootstrap.ts | 1 + bricks/data-view/src/indicator-card/i18n.ts | 18 +++ .../src/indicator-card/index.spec.tsx | 127 ++++++++++++++++++ bricks/data-view/src/indicator-card/index.tsx | 90 +++++++++++++ .../src/indicator-card/styles.shadow.css | 98 ++++++++++++++ 10 files changed, 409 insertions(+) create mode 100644 bricks/data-view/docs/indicator-card.md create mode 100644 bricks/data-view/src/asset/images/block.png create mode 100644 bricks/data-view/src/asset/images/colBg.png create mode 100644 bricks/data-view/src/asset/images/divider.png create mode 100644 bricks/data-view/src/asset/images/rowBg.png create mode 100644 bricks/data-view/src/indicator-card/i18n.ts create mode 100644 bricks/data-view/src/indicator-card/index.spec.tsx create mode 100644 bricks/data-view/src/indicator-card/index.tsx create mode 100644 bricks/data-view/src/indicator-card/styles.shadow.css 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 0000000000000000000000000000000000000000..6e3bc457b7fc4c229685c6d161ca031dc32bba52 GIT binary patch literal 720 zcmV;>0x$iEP)P000;W1^@s654Bdt00001b5ch_0Itp) z=>Px%ib+I4R7gw3mP=1lK@`W&nS0N)y-->TEe{ioF4RO5G(Hj&6SsZ=KY<%pA}#>g zh{VU%jiRC}H@b1*%7w5Ih>5%w5=99y7)&S?#om_NJ9Fol@=)F}(bic^GG`vYGc#xY zXAsIzunY|NLkJ`A26vWb;zsM1K5q|UUhTt902qh$4jlsEq8(rbkJ|$`L8z_&E(gH{ zM8IWdm#gK*`ohdhh~jouw)B-`tZDJak(sq4H$z%qjfUg3kDcn|m?WcxVC^`zp>T0U z00YLWEJK!G zRHw+q6J*6gnnDiQ7J_965R^8`*vef{I-6yk$td_S=h5f!$sQzmX-7yWv$%C-U?`fb z>E3;~VWd7u7)u~kR*;ktTLK0o05D)H&`K8ydF5wa+fS$SW`0)NQ^ONIK-A)Oo#CT9t_dt+|}c~{})pF;{t%vU;L5^ap^-Cn6Vs>u?(^T z090~;EJDg5^}p1!lu2vroL{Q+`wxE@fB2F3JK|4wb{IDr0*BoI0000Px$2vAH^MF0SR0DypifPerqW-~KpGiGKWK!89%fEO-wE=F`NMvEjjfFwzP9z2H{ zK#UkYh7>)86g`h0J%t}Vj~h3M8$gL0K$03nkr_gaAwrZFJdYkcj~+vo96*&EK9L+g zm>flz8bFg8K$#djnixTv7DJmAKAaUqoD@Kv6hNXBMWPcyq7p!)7eJ&EKBp2wrxrq| z5ksdHL#Gu&r4K@?6GEyIL#`4)u@XbE5k#>OL$VM-v=BkJ5Jk5TLbnh_w-7|M4@0>R zL%a_|zYj*d4n)2VL%a?}zz#*j4MoBYMa2z7!wp8p4MoHXMac|B$qGcw3P#KfM#>CE z%nC-z3`WWcM$QaI&K1o4jQ{`uSaecOQvw141qB5N2n!1f4h|6%6&V>B85|rO9v>ef zA|fOuB_${*D=RKBF*GzZH#j&tIyyZ)K0-o8NJvXePEJlyQBqV_S65nFU0q;cVPj-x zXK86^X=-Y2Zj<0^qyPW`a!Eu%R9M5smqB*JAPhujgxXc_f7@7H07ABLHj!EuiJm`x znIH`U3UGiR3;W@oOK{RH=obiHqXC2_%WNGU3K<^LEd`iGp^h_fA9WGu8g_A z&ofl<<>$4t~capgx0)x3KhHjqE&%b={ zEP^WjpZ$5N+4YsEHk&I~R_>oRa#O))p5aRE@@5rRiOT4%i*XHK|9R#mEW#3Ht2HT)MaPx$07*naR47wjk>M&taTLVoe>cjKB+^C{Mbs9xwMtP$5v3#_Xg?s4yn`oLwRZ2` zjVJJm+8rHZDBpg~oik_t=O#Qe1Av4chXhTI2s#`Syl}#vDZv|O1YOSh(>y+r5^^p| zX>-{*tAbZnt*l$A*<5$v76xF9Z(|UFUGCbmFUWal^D+8TPOY4I#f9M#DFQoeIOp0q zw*ayjzeiH}M=MVNN}&XNzw|+Ua^5-l#?bKi)xYgI&ymIU--ZQG)Va+gn-5%U&&78B zuJQBaO(_Ly=<6&SzI;_m!8MPs2wGecymLW+r^Pu>RqUA-G&yNy9HY^>$Fh_U4jW1V YU)6_x!teu{djJ3c07*qoM6N<$g7bWb!~g&Q literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6f88b97b9b72f2879018b9879268b60f2b55c4d9 GIT binary patch literal 8140 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 ({})); + +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; +} From 5334d42cb5fae1da762f74c271d7a5295ee24d7d Mon Sep 17 00:00:00 2001 From: alexchen Date: Thu, 1 Feb 2024 18:07:09 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat():=20=E6=96=B0=E5=A2=9Eeo-snapshot?= =?UTF-8?q?=E6=9E=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes CMDB_VIEW-100 --- bricks/basic/docs/eo-snapshot.md | 51 ++++++++++ bricks/basic/package.json | 2 + bricks/basic/src/bootstrap.ts | 1 + bricks/basic/src/snapshot/i18n.ts | 18 ++++ bricks/basic/src/snapshot/index.spec.tsx | 24 +++++ bricks/basic/src/snapshot/index.tsx | 103 ++++++++++++++++++++ bricks/basic/src/snapshot/snapshot.ts | 91 +++++++++++++++++ bricks/basic/src/snapshot/styles.shadow.css | 7 ++ shared/common-bricks/common-bricks.json | 3 +- yarn.lock | 97 +++++++++++++++++- 10 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 bricks/basic/docs/eo-snapshot.md create mode 100644 bricks/basic/src/snapshot/i18n.ts create mode 100644 bricks/basic/src/snapshot/index.spec.tsx create mode 100644 bricks/basic/src/snapshot/index.tsx create mode 100644 bricks/basic/src/snapshot/snapshot.ts create mode 100644 bricks/basic/src/snapshot/styles.shadow.css 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/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"