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;
+ }
+ `;
}
}