mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
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 <balloob@gmail.com> --------- Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
b69f0964c9
commit
7aa7019386
155
src/components/ha-badge.ts
Normal file
155
src/components/ha-badge.ts
Normal file
@ -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`
|
||||||
|
<div
|
||||||
|
class="badge ${classMap({
|
||||||
|
"icon-only": this.iconOnly,
|
||||||
|
})}"
|
||||||
|
role=${ifDefined(this.type === "button" ? "button" : undefined)}
|
||||||
|
tabindex=${ifDefined(this.type === "button" ? "0" : undefined)}
|
||||||
|
>
|
||||||
|
<ha-ripple .disabled=${this.type !== "button"}></ha-ripple>
|
||||||
|
<slot name="icon"></slot>
|
||||||
|
${this.iconOnly
|
||||||
|
? nothing
|
||||||
|
: html`<span class="info">
|
||||||
|
${label ? html`<span class="label">${label}</span>` : nothing}
|
||||||
|
<span class="content"><slot></slot></span>
|
||||||
|
</span>`}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,6 @@ import { HassEntity } from "home-assistant-js-websocket";
|
|||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { computeCssColor } from "../../../common/color/compute-color";
|
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 { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||||
import { stateActive } from "../../../common/entity/state_active";
|
import { stateActive } from "../../../common/entity/state_active";
|
||||||
import { stateColorCss } from "../../../common/entity/state_color";
|
import { stateColorCss } from "../../../common/entity/state_color";
|
||||||
|
import "../../../components/ha-badge";
|
||||||
import "../../../components/ha-ripple";
|
import "../../../components/ha-ripple";
|
||||||
import "../../../components/ha-state-icon";
|
import "../../../components/ha-state-icon";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
@ -160,15 +160,14 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
|||||||
|
|
||||||
if (!stateObj) {
|
if (!stateObj) {
|
||||||
return html`
|
return html`
|
||||||
<div class="badge error">
|
<ha-badge .label=${entityId} class="error">
|
||||||
<ha-svg-icon .hass=${this.hass} .path=${mdiAlertCircle}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
<span class="info">
|
slot="icon"
|
||||||
<span class="label">${entityId}</span>
|
.hass=${this.hass}
|
||||||
<span class="content">
|
.path=${mdiAlertCircle}
|
||||||
${this.hass.localize("ui.badge.entity.not_found")}
|
></ha-svg-icon>
|
||||||
</span>
|
${this.hass.localize("ui.badge.entity.not_found")}
|
||||||
</span>
|
</ha-badge>
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,42 +203,32 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
|||||||
const content = showState ? stateDisplay : showName ? name : undefined;
|
const content = showState ? stateDisplay : showName ? name : undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<ha-badge
|
||||||
style=${styleMap(style)}
|
.type=${this.hasAction ? "button" : "badge"}
|
||||||
class="badge ${classMap({
|
|
||||||
active,
|
|
||||||
"no-info": !showState && !showName,
|
|
||||||
"no-icon": !showIcon,
|
|
||||||
})}"
|
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
.actionHandler=${actionHandler({
|
.actionHandler=${actionHandler({
|
||||||
hasHold: hasAction(this._config!.hold_action),
|
hasHold: hasAction(this._config!.hold_action),
|
||||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||||
})}
|
})}
|
||||||
role=${ifDefined(this.hasAction ? "button" : undefined)}
|
.label=${label}
|
||||||
tabindex=${ifDefined(this.hasAction ? "0" : undefined)}
|
.iconOnly=${!content}
|
||||||
|
style=${styleMap(style)}
|
||||||
|
class=${classMap({ active })}
|
||||||
>
|
>
|
||||||
<ha-ripple .disabled=${!this.hasAction}></ha-ripple>
|
|
||||||
${showIcon
|
${showIcon
|
||||||
? imageUrl
|
? imageUrl
|
||||||
? html`<img src=${imageUrl} aria-hidden />`
|
? html`<img slot="icon" src=${imageUrl} aria-hidden />`
|
||||||
: html`
|
: html`
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
|
slot="icon"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.icon=${this._config.icon}
|
.icon=${this._config.icon}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
${content
|
${content}
|
||||||
? html`
|
</ha-badge>
|
||||||
<span class="info">
|
|
||||||
${label ? html`<span class="label">${name}</span>` : nothing}
|
|
||||||
<span class="content">${content}</span>
|
|
||||||
</span>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,119 +238,15 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
|||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
ha-badge {
|
||||||
--badge-color: var(--state-inactive-color);
|
--badge-color: var(--state-inactive-color);
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
}
|
||||||
.badge.error {
|
ha-badge.error {
|
||||||
--badge-color: var(--red-color);
|
--badge-color: var(--red-color);
|
||||||
}
|
}
|
||||||
.badge {
|
ha-badge.active {
|
||||||
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 {
|
|
||||||
--badge-color: var(--primary-color);
|
--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;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,11 @@ import { mdiAlertCircle } from "@mdi/js";
|
|||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import "../../../components/ha-label-badge";
|
import "../../../components/ha-badge";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { showAlertDialog } from "../custom-card-helpers";
|
import { showAlertDialog } from "../custom-card-helpers";
|
||||||
import { LovelaceBadge } from "../types";
|
import { LovelaceBadge } from "../types";
|
||||||
import { HuiEntityBadge } from "./hui-entity-badge";
|
|
||||||
import { ErrorBadgeConfig } from "./types";
|
import { ErrorBadgeConfig } from "./types";
|
||||||
|
|
||||||
export const createErrorBadgeElement = (config) => {
|
export const createErrorBadgeElement = (config) => {
|
||||||
@ -55,41 +54,36 @@ export class HuiErrorBadge extends LitElement implements LovelaceBadge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<button class="badge error" @click=${this._viewDetail}>
|
<ha-badge
|
||||||
<ha-svg-icon .hass=${this.hass} .path=${mdiAlertCircle}></ha-svg-icon>
|
class="error"
|
||||||
<ha-ripple></ha-ripple>
|
@click=${this._viewDetail}
|
||||||
<span class="content">
|
type="button"
|
||||||
<span class="name">Error</span>
|
label="Error"
|
||||||
<span class="state">${this._config.error}</span>
|
>
|
||||||
</span>
|
<ha-svg-icon slot="icon" .path=${mdiAlertCircle}></ha-svg-icon>
|
||||||
</button>
|
<div class="content">${this._config.error}</div>
|
||||||
|
</ha-badge>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return css`
|
||||||
HuiEntityBadge.styles,
|
ha-badge {
|
||||||
css`
|
--badge-color: var(--error-color);
|
||||||
.badge.error {
|
--ha-card-border-color: var(--error-color);
|
||||||
--badge-color: var(--error-color);
|
}
|
||||||
border-color: var(--badge-color);
|
.content {
|
||||||
}
|
max-width: 100px;
|
||||||
ha-svg-icon {
|
overflow: hidden;
|
||||||
color: var(--badge-color);
|
text-overflow: ellipsis;
|
||||||
}
|
white-space: nowrap;
|
||||||
.state {
|
}
|
||||||
max-width: 100px;
|
pre {
|
||||||
overflow: hidden;
|
font-family: var(--code-font-family, monospace);
|
||||||
text-overflow: ellipsis;
|
white-space: break-spaces;
|
||||||
white-space: nowrap;
|
user-select: text;
|
||||||
}
|
}
|
||||||
pre {
|
`;
|
||||||
font-family: var(--code-font-family, monospace);
|
|
||||||
white-space: break-spaces;
|
|
||||||
user-select: text;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user