diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 49839b27f660..af8a33890396 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -64,7 +64,7 @@ jobs: - name: Install dependencies run: yarn install --immutable - name: Build resources - run: ./node_modules/.bin/gulp build-translations build-locale-data + run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data - name: Run Tests run: yarn run test build: diff --git a/hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts b/hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts index 8a1c3c670221..4dcfde2ec2b4 100644 --- a/hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts +++ b/hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts @@ -4,7 +4,6 @@ import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../src/common/dom/fire_event"; import "../../../../src/components/ha-circular-progress"; -import "../../../../src/components/ha-markdown"; import "../../../../src/components/ha-select"; import { extractApiErrorMessage, diff --git a/src/components/ha-markdown-element.ts b/src/components/ha-markdown-element.ts index e00bfceb9477..0e806b7c7dcf 100644 --- a/src/components/ha-markdown-element.ts +++ b/src/components/ha-markdown-element.ts @@ -95,6 +95,15 @@ class HaMarkdownElement extends ReactiveElement { } node.firstElementChild!.replaceWith(alertNote); } + } else if ( + node instanceof HTMLElement && + ["ha-alert", "ha-qr-code", "ha-icon", "ha-svg-icon"].includes( + node.localName + ) + ) { + import( + /* webpackInclude: /(ha-alert)|(ha-qr-code)|(ha-icon)|(ha-svg-icon)/ */ `./${node.localName}` + ); } } } diff --git a/src/components/ha-markdown.ts b/src/components/ha-markdown.ts index c248a320e819..f81ddd286037 100644 --- a/src/components/ha-markdown.ts +++ b/src/components/ha-markdown.ts @@ -2,11 +2,6 @@ import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import "./ha-markdown-element"; -// Import components that are allwoed to be defined. -import "./ha-alert"; -import "./ha-icon"; -import "./ha-svg-icon"; - @customElement("ha-markdown") export class HaMarkdown extends LitElement { @property() public content?; diff --git a/src/components/ha-qr-code.ts b/src/components/ha-qr-code.ts new file mode 100644 index 000000000000..f6a686d06823 --- /dev/null +++ b/src/components/ha-qr-code.ts @@ -0,0 +1,114 @@ +import { LitElement, PropertyValues, css, html, nothing } from "lit"; +import { customElement, property, query, state } from "lit/decorators"; +import QRCode from "qrcode"; + +@customElement("ha-qr-code") +export class HaQrCode extends LitElement { + @property() public data?: string; + + @property({ attribute: "error-correction-level" }) + public errorCorrectionLevel: "low" | "medium" | "quartile" | "high" = + "medium"; + + @property({ type: Number }) + public width = 4; + + @property({ type: Number }) + public scale = 4; + + @property({ type: Number }) + public margin = 4; + + @property({ type: Number }) public maskPattern?: + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7; + + @property({ attribute: "center-image" }) public centerImage?: string; + + @state() private _error?: string; + + @query("canvas") private _canvas?: HTMLCanvasElement; + + protected willUpdate(changedProperties: PropertyValues): void { + super.willUpdate(changedProperties); + if ( + (changedProperties.has("data") || + changedProperties.has("scale") || + changedProperties.has("width") || + changedProperties.has("margin") || + changedProperties.has("maskPattern") || + changedProperties.has("errorCorrectionLevel")) && + this._error + ) { + this._error = undefined; + } + } + + updated(changedProperties: PropertyValues) { + const canvas = this._canvas; + if ( + canvas && + this.data && + (changedProperties.has("data") || + changedProperties.has("scale") || + changedProperties.has("width") || + changedProperties.has("margin") || + changedProperties.has("maskPattern") || + changedProperties.has("errorCorrectionLevel") || + changedProperties.has("centerImage")) + ) { + const computedStyles = getComputedStyle(this); + + QRCode.toCanvas(canvas, this.data, { + errorCorrectionLevel: this.errorCorrectionLevel, + width: this.width, + scale: this.scale, + margin: this.margin, + maskPattern: this.maskPattern, + color: { + light: computedStyles.getPropertyValue("--card-background-color"), + dark: computedStyles.getPropertyValue("--primary-text-color"), + }, + }).catch((err) => { + this._error = err.message; + }); + + if (this.centerImage) { + const context = this._canvas!.getContext("2d"); + const imageObj = new Image(); + imageObj.src = this.centerImage; + imageObj.onload = () => { + context?.drawImage( + imageObj, + canvas.width * 0.375, + canvas.height * 0.375, + canvas.width / 4, + canvas.height / 4 + ); + }; + } + } + } + + render() { + if (!this.data) { + return nothing; + } + if (this._error) { + return html`${this._error}`; + } + return html``; + } + + static styles = css` + :host { + display: block; + } + `; +} diff --git a/src/panels/config/automation/manual-automation-editor.ts b/src/panels/config/automation/manual-automation-editor.ts index 291d30854011..811ea2c3fd0a 100644 --- a/src/panels/config/automation/manual-automation-editor.ts +++ b/src/panels/config/automation/manual-automation-editor.ts @@ -7,6 +7,7 @@ import { ensureArray } from "../../../common/array/ensure-array"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-card"; import "../../../components/ha-icon-button"; +import "../../../components/ha-markdown"; import { Condition, ManualAutomationConfig, diff --git a/src/panels/config/storage/dialog-move-datadisk.ts b/src/panels/config/storage/dialog-move-datadisk.ts index 456c9884e7db..7f5495a8e9dc 100644 --- a/src/panels/config/storage/dialog-move-datadisk.ts +++ b/src/panels/config/storage/dialog-move-datadisk.ts @@ -5,7 +5,6 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; import { stopPropagation } from "../../../common/dom/stop_propagation"; import "../../../components/ha-circular-progress"; -import "../../../components/ha-markdown"; import "../../../components/ha-select"; import { extractApiErrorMessage, diff --git a/src/panels/config/tags/dialog-tag-detail.ts b/src/panels/config/tags/dialog-tag-detail.ts index 7f402f2d4a4d..62c0e6fe3f33 100644 --- a/src/panels/config/tags/dialog-tag-detail.ts +++ b/src/panels/config/tags/dialog-tag-detail.ts @@ -1,17 +1,11 @@ import "@material/mwc-button"; -import { - css, - CSSResultGroup, - html, - LitElement, - TemplateResult, - nothing, -} from "lit"; +import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-alert"; import { createCloseHeading } from "../../../components/ha-dialog"; import "../../../components/ha-formfield"; +import "../../../components/ha-qr-code"; import "../../../components/ha-switch"; import "../../../components/ha-textfield"; import { Tag, UpdateTagParams } from "../../../data/tag"; @@ -20,8 +14,6 @@ import { haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { TagDetailDialogParams } from "./show-dialog-tag-detail"; -const QR_LOGO_URL = "/static/icons/favicon-192x192.png"; - @customElement("dialog-tag-detail") class DialogTagDetail extends LitElement @@ -39,8 +31,6 @@ class DialogTagDetail @state() private _submitting = false; - @state() private _qrCode?: TemplateResult; - public showDialog(params: TagDetailDialogParams): void { this._params = params; this._error = undefined; @@ -50,13 +40,10 @@ class DialogTagDetail this._id = ""; this._name = ""; } - - this._generateQR(); } public closeDialog(): void { this._params = undefined; - this._qrCode = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -130,9 +117,15 @@ class DialogTagDetail })}

- ${this._qrCode - ? html`
${this._qrCode}
` - : ""} +
+ + +
` : ``} @@ -158,7 +151,7 @@ class DialogTagDetail : this.hass!.localize("ui.panel.config.tag.detail.create")} ${this._params.openWrite && !this._params.entry - ? html` { - imageObj.onload = resolve; - }); - context?.drawImage( - imageObj, - canvas.width / 3, - canvas.height / 3, - canvas.width / 3, - canvas.height / 3 - ); - - this._qrCode = html`${this.hass.localize("ui.panel.config.tag.qr_code_image",`; - } - static get styles(): CSSResultGroup { return [ haStyleDialog, @@ -270,6 +228,9 @@ class DialogTagDetail display: block; margin: 8px 0; } + ::slotted(img) { + height: 100%; + } `, ]; } diff --git a/src/resources/markdown-worker.ts b/src/resources/markdown-worker.ts index a2e2234ecaba..85949e3f2307 100644 --- a/src/resources/markdown-worker.ts +++ b/src/resources/markdown-worker.ts @@ -42,6 +42,14 @@ const renderMarkdown = async ( "ha-icon": ["icon"], "ha-svg-icon": ["path"], "ha-alert": ["alert-type", "title"], + "ha-qr-code": [ + "data", + "scale", + "width", + "margin", + "error-correction-level", + "center-image", + ], }; }