From 7aa7019386404f92a2f37c6d78103f8e9504f5c1 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 4 Sep 2024 10:48:55 +0200 Subject: [PATCH] Move badge styling into ha-badge component to reuse it (#21864) * Move badge styling into ha-badge component to reuse it * Fix error badge * Update src/components/ha-badge.ts Co-authored-by: Paulus Schoutsen --------- Co-authored-by: Paulus Schoutsen --- src/components/ha-badge.ts | 155 +++++++++++++++++ .../lovelace/badges/hui-entity-badge.ts | 159 +++--------------- src/panels/lovelace/badges/hui-error-badge.ts | 60 +++---- 3 files changed, 204 insertions(+), 170 deletions(-) create mode 100644 src/components/ha-badge.ts diff --git a/src/components/ha-badge.ts b/src/components/ha-badge.ts new file mode 100644 index 0000000000..66a56b63fc --- /dev/null +++ b/src/components/ha-badge.ts @@ -0,0 +1,155 @@ +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { ifDefined } from "lit/directives/if-defined"; +import "./ha-ripple"; + +type BadgeType = "badge" | "button"; + +@customElement("ha-badge") +export class HaBadge extends LitElement { + @property() public type: BadgeType = "badge"; + + @property() public label?: string; + + @property({ type: Boolean, attribute: "icon-only" }) iconOnly = false; + + protected render() { + const label = this.label; + + return html` +
+ + + ${this.iconOnly + ? nothing + : html` + ${label ? html`${label}` : nothing} + + `} +
+ `; + } + + static get styles(): CSSResultGroup { + return css` + :host { + --badge-color: var(--secondary-text-color); + -webkit-tap-highlight-color: transparent; + } + .badge { + position: relative; + --ha-ripple-color: var(--badge-color); + --ha-ripple-hover-opacity: 0.04; + --ha-ripple-pressed-opacity: 0.12; + transition: + box-shadow 180ms ease-in-out, + border-color 180ms ease-in-out; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 8px; + height: var(--ha-badge-size, 36px); + min-width: var(--ha-badge-size, 36px); + padding: 0px 12px; + box-sizing: border-box; + width: auto; + border-radius: var( + --ha-badge-border-radius, + calc(var(--ha-badge-size, 36px) / 2) + ); + background: var( + --ha-card-background, + var(--card-background-color, white) + ); + -webkit-backdrop-filter: var(--ha-card-backdrop-filter, none); + backdrop-filter: var(--ha-card-backdrop-filter, none); + border-width: var(--ha-card-border-width, 1px); + box-shadow: var(--ha-card-box-shadow, none); + border-style: solid; + border-color: var( + --ha-card-border-color, + var(--divider-color, #e0e0e0) + ); + } + .badge:focus-visible { + --shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent); + --shadow-focus: 0 0 0 1px var(--badge-color); + border-color: var(--badge-color); + box-shadow: var(--shadow-default), var(--shadow-focus); + } + [role="button"] { + cursor: pointer; + } + [role="button"]:focus { + outline: none; + } + .info { + display: flex; + flex-direction: column; + align-items: flex-start; + padding-inline-start: initial; + text-align: center; + font-family: Roboto; + } + .label { + font-size: 10px; + font-style: normal; + font-weight: 500; + line-height: 10px; + letter-spacing: 0.1px; + color: var(--secondary-text-color); + } + .content { + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 16px; + letter-spacing: 0.1px; + color: var(--primary-text-color); + } + ::slotted([slot="icon"]) { + --mdc-icon-size: 18px; + color: var(--badge-color); + line-height: 0; + margin-left: -4px; + margin-right: 0; + margin-inline-start: -4px; + margin-inline-end: 0; + } + ::slotted(img[slot="icon"]) { + width: 30px; + height: 30px; + border-radius: 50%; + object-fit: cover; + overflow: hidden; + margin-left: -10px; + margin-right: 0; + margin-inline-start: -10px; + margin-inline-end: 0; + } + .badge.icon-only { + padding: 0; + } + .badge.icon-only ::slotted([slot="icon"]) { + margin-left: 0; + margin-right: 0; + margin-inline-start: 0; + margin-inline-end: 0; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-badge": HaBadge; + } +} diff --git a/src/panels/lovelace/badges/hui-entity-badge.ts b/src/panels/lovelace/badges/hui-entity-badge.ts index 39aa9a193f..5bf3b26d13 100644 --- a/src/panels/lovelace/badges/hui-entity-badge.ts +++ b/src/panels/lovelace/badges/hui-entity-badge.ts @@ -3,7 +3,6 @@ import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; import { computeCssColor } from "../../../common/color/compute-color"; @@ -12,6 +11,7 @@ import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { stateActive } from "../../../common/entity/state_active"; import { stateColorCss } from "../../../common/entity/state_color"; +import "../../../components/ha-badge"; import "../../../components/ha-ripple"; import "../../../components/ha-state-icon"; import "../../../components/ha-svg-icon"; @@ -160,15 +160,14 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge { if (!stateObj) { return html` -
- - - ${entityId} - - ${this.hass.localize("ui.badge.entity.not_found")} - - -
+ + + ${this.hass.localize("ui.badge.entity.not_found")} + `; } @@ -204,42 +203,32 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge { const content = showState ? stateDisplay : showName ? name : undefined; return html` -
- ${showIcon ? imageUrl - ? html`` + ? html`` : html` ` : nothing} - ${content - ? html` - - ${label ? html`${name}` : nothing} - ${content} - - ` - : nothing} -
+ ${content} + `; } @@ -249,119 +238,15 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge { static get styles(): CSSResultGroup { return css` - :host { + ha-badge { --badge-color: var(--state-inactive-color); - -webkit-tap-highlight-color: transparent; } - .badge.error { + ha-badge.error { --badge-color: var(--red-color); } - .badge { - position: relative; - --ha-ripple-color: var(--badge-color); - --ha-ripple-hover-opacity: 0.04; - --ha-ripple-pressed-opacity: 0.12; - transition: - box-shadow 180ms ease-in-out, - border-color 180ms ease-in-out; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - gap: 8px; - height: var(--ha-badge-size, 36px); - min-width: var(--ha-badge-size, 36px); - padding: 0px 8px; - box-sizing: border-box; - width: auto; - border-radius: var( - --ha-badge-border-radius, - calc(var(--ha-badge-size, 36px) / 2) - ); - background: var( - --ha-card-background, - var(--card-background-color, white) - ); - -webkit-backdrop-filter: var(--ha-card-backdrop-filter, none); - backdrop-filter: var(--ha-card-backdrop-filter, none); - border-width: var(--ha-card-border-width, 1px); - box-shadow: var(--ha-card-box-shadow, none); - border-style: solid; - border-color: var( - --ha-card-border-color, - var(--divider-color, #e0e0e0) - ); - --mdc-icon-size: 18px; - text-align: center; - font-family: Roboto; - } - .badge:focus-visible { - --shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent); - --shadow-focus: 0 0 0 1px var(--badge-color); - border-color: var(--badge-color); - box-shadow: var(--shadow-default), var(--shadow-focus); - } - button, - [role="button"] { - cursor: pointer; - } - button:focus, - [role="button"]:focus { - outline: none; - } - .badge.active { + ha-badge.active { --badge-color: var(--primary-color); } - .info { - display: flex; - flex-direction: column; - align-items: flex-start; - padding-right: 4px; - padding-inline-end: 4px; - padding-inline-start: initial; - } - .label { - font-size: 10px; - font-style: normal; - font-weight: 500; - line-height: 10px; - letter-spacing: 0.1px; - color: var(--secondary-text-color); - } - .content { - font-size: 12px; - font-style: normal; - font-weight: 500; - line-height: 16px; - letter-spacing: 0.1px; - color: var(--primary-text-color); - } - ha-state-icon, - ha-svg-icon { - color: var(--badge-color); - line-height: 0; - } - img { - width: 30px; - height: 30px; - border-radius: 50%; - object-fit: cover; - overflow: hidden; - } - .badge.no-info { - padding: 0; - } - .badge:not(.no-icon):not(.no-info) img { - margin-left: -6px; - margin-inline-start: -6px; - margin-inline-end: initial; - } - .badge.no-icon .info { - padding-right: 4px; - padding-left: 4px; - padding-inline-end: 4px; - padding-inline-start: 4px; - } `; } } diff --git a/src/panels/lovelace/badges/hui-error-badge.ts b/src/panels/lovelace/badges/hui-error-badge.ts index 19c6e95826..1df21d7d04 100644 --- a/src/panels/lovelace/badges/hui-error-badge.ts +++ b/src/panels/lovelace/badges/hui-error-badge.ts @@ -2,12 +2,11 @@ import { mdiAlertCircle } from "@mdi/js"; import { dump } from "js-yaml"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, state } from "lit/decorators"; -import "../../../components/ha-label-badge"; +import "../../../components/ha-badge"; import "../../../components/ha-svg-icon"; import { HomeAssistant } from "../../../types"; import { showAlertDialog } from "../custom-card-helpers"; import { LovelaceBadge } from "../types"; -import { HuiEntityBadge } from "./hui-entity-badge"; import { ErrorBadgeConfig } from "./types"; export const createErrorBadgeElement = (config) => { @@ -55,41 +54,36 @@ export class HuiErrorBadge extends LitElement implements LovelaceBadge { } return html` - + + +
${this._config.error}
+
`; } static get styles(): CSSResultGroup { - return [ - HuiEntityBadge.styles, - css` - .badge.error { - --badge-color: var(--error-color); - border-color: var(--badge-color); - } - ha-svg-icon { - color: var(--badge-color); - } - .state { - max-width: 100px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - pre { - font-family: var(--code-font-family, monospace); - white-space: break-spaces; - user-select: text; - } - `, - ]; + return css` + ha-badge { + --badge-color: var(--error-color); + --ha-card-border-color: var(--error-color); + } + .content { + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + pre { + font-family: var(--code-font-family, monospace); + white-space: break-spaces; + user-select: text; + } + `; } }