From f3606014c66eaa33e8c3fe63da6d3a2a490abc68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 24 Aug 2021 18:44:30 +0200 Subject: [PATCH] Add ha-alert component (#9874) --- gallery/src/demos/demo-ha-alert.ts | 144 +++++++++++++++++++++++ gallery/src/ha-gallery.js | 8 ++ src/components/ha-alert.ts | 176 +++++++++++++++++++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 gallery/src/demos/demo-ha-alert.ts create mode 100644 src/components/ha-alert.ts diff --git a/gallery/src/demos/demo-ha-alert.ts b/gallery/src/demos/demo-ha-alert.ts new file mode 100644 index 0000000000..91e421522b --- /dev/null +++ b/gallery/src/demos/demo-ha-alert.ts @@ -0,0 +1,144 @@ +import { html, css, LitElement, TemplateResult } from "lit"; +import { customElement } from "lit/decorators"; +import "../../../src/components/ha-alert"; +import "../../../src/components/ha-card"; + +const alerts: { + title?: string; + description: string | TemplateResult; + type: "info" | "warning" | "error" | "success"; + dismissable?: boolean; + action?: string; + rtl?: boolean; +}[] = [ + { + title: "Test info alert", + description: "This is a test info alert with a title and description", + type: "info", + }, + { + title: "Test warning alert", + description: "This is a test warning alert with a title and description", + type: "warning", + }, + { + title: "Test error alert", + description: "This is a test error alert with a title and description", + type: "error", + }, + { + title: "Test success alert", + description: "This is a test success alert with a title and description", + type: "success", + }, + { + description: "This is a test info alert with description only", + type: "info", + }, + { + description: + "This is a test warning alert with a rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really long description only", + type: "warning", + }, + { + title: "Error with description and list", + description: html`

+ This is a test error alert with a title, description and a list +

+ `, + type: "error", + }, + { + title: "Test dismissable alert", + description: "This is a test success alert that can be dismissable", + type: "success", + dismissable: true, + }, + { + description: "Dismissable information", + type: "info", + dismissable: true, + }, + { + title: "Error with action", + description: "This is a test error alert with action", + type: "error", + action: "restart", + }, + { + title: "Unsaved data", + description: "You have unsaved data", + type: "warning", + action: "save", + }, + { + description: "Dismissable information (RTL)", + type: "info", + dismissable: true, + rtl: true, + }, + { + title: "Error with action", + description: "This is a test error alert with action (RTL)", + type: "error", + action: "restart", + rtl: true, + }, + { + title: "Test success alert (RTL)", + description: "This is a test success alert with a title and description", + type: "success", + rtl: true, + }, +]; + +@customElement("demo-ha-alert") +export class DemoHaAlert extends LitElement { + protected render(): TemplateResult { + return html` + + ${alerts.map( + (alert) => html` + + ${alert.description} + + ` + )} + + `; + } + + static get styles() { + return css` + ha-card { + max-width: 600px; + margin: 24px auto; + } + .condition { + padding: 16px; + display: flex; + align-items: center; + justify-content: space-between; + } + span { + margin-right: 16px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-ha-alert": DemoHaAlert; + } +} diff --git a/gallery/src/ha-gallery.js b/gallery/src/ha-gallery.js index f94fd0ab53..9d09da0119 100644 --- a/gallery/src/ha-gallery.js +++ b/gallery/src/ha-gallery.js @@ -172,6 +172,14 @@ class HaGallery extends PolymerElement { this.$.notifications.showDialog({ message: ev.detail.message }) ); + this.addEventListener("alert-dismissed-clicked", () => + this.$.notifications.showDialog({ message: "Alert dismissed clicked" }) + ); + + this.addEventListener("alert-action-clicked", () => + this.$.notifications.showDialog({ message: "Alert action clicked" }) + ); + this.addEventListener("hass-more-info", (ev) => { if (ev.detail.entityId) { this.$.notifications.showDialog({ diff --git a/src/components/ha-alert.ts b/src/components/ha-alert.ts new file mode 100644 index 0000000000..718e2a9971 --- /dev/null +++ b/src/components/ha-alert.ts @@ -0,0 +1,176 @@ +import "@material/mwc-button/mwc-button"; +import "@material/mwc-icon-button/mwc-icon-button"; +import { + mdiAlertCircleOutline, + mdiAlertOutline, + mdiCheckboxMarkedCircleOutline, + mdiClose, +} from "@mdi/js"; +import { css, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { fireEvent } from "../common/dom/fire_event"; +import "./ha-svg-icon"; + +const ALERT_ICONS = { + info: mdiAlertCircleOutline, + warning: mdiAlertOutline, + error: mdiAlertCircleOutline, + success: mdiCheckboxMarkedCircleOutline, +}; + +declare global { + interface HASSDomEvents { + "alert-dismissed-clicked": undefined; + "alert-action-clicked": undefined; + } +} + +@customElement("ha-alert") +class HaAlert extends LitElement { + @property() public title = ""; + + @property() public alertType: "info" | "warning" | "error" | "success" = + "info"; + + @property({ attribute: "action-text" }) public actionText = ""; + + @property({ type: Boolean }) public dismissable = false; + + @property({ type: Boolean }) public rtl = false; + + public render() { + return html` +
+
+ +
+
+
+ ${this.title ? html`
${this.title}
` : ""} + +
+
+ ${this.actionText + ? html`` + : this.dismissable + ? html` + + ` + : ""} +
+
+
+ `; + } + + private _dismiss_clicked() { + fireEvent(this, "alert-dismissed-clicked"); + } + + private _action_clicked() { + fireEvent(this, "alert-action-clicked"); + } + + static styles = css` + .issue-type { + position: relative; + padding: 4px; + display: flex; + margin: 4px 0; + } + .issue-type.rtl { + flex-direction: row-reverse; + } + .issue-type::before { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + opacity: 0.12; + pointer-events: none; + content: ""; + border-radius: 4px; + } + .icon { + margin: 4px 8px; + width: 24px; + } + .main-content.no-title { + margin-top: 6px; + } + .issue-type.rtl > .content { + flex-direction: row-reverse; + text-align: right; + } + .content { + display: flex; + justify-content: space-between; + width: 100%; + } + .title { + font-weight: bold; + margin-top: 6px; + } + + mwc-button { + --mdc-theme-primary: var(--primary-text-color); + } + + .action { + align-self: center; + } + + .issue-type.info > .icon { + color: var(--info-color); + } + .issue-type.info::before { + background-color: var(--info-color); + } + + .issue-type.warning > .icon { + color: var(--warning-color); + } + .issue-type.warning::before { + background-color: var(--warning-color); + } + + .issue-type.error > .icon { + color: var(--error-color); + } + .issue-type.error::before { + background-color: var(--error-color); + } + + .issue-type.success > .icon { + color: var(--success-color); + } + .issue-type.success::before { + background-color: var(--success-color); + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-alert": HaAlert; + } +}