20240904.0 (#21876)

This commit is contained in:
Bram Kragten 2024-09-04 10:59:24 +02:00 committed by GitHub
commit b457d27c4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 355 additions and 281 deletions

View File

@ -15,23 +15,29 @@ const brotliOptions = {
}; };
const zopfliOptions = { threshold: 150 }; const zopfliOptions = { threshold: 150 };
const compressDistBrotli = (rootDir, modernDir) => const compressDistBrotli = (rootDir, modernDir, compressServiceWorker = true) =>
gulp gulp
.src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], { .src(
base: rootDir, [
}) `${modernDir}/**/${filesGlob}`,
compressServiceWorker ? `${rootDir}/sw-modern.js` : undefined,
].filter(Boolean),
{
base: rootDir,
}
)
.pipe(brotli(brotliOptions)) .pipe(brotli(brotliOptions))
.pipe(gulp.dest(rootDir)); .pipe(gulp.dest(rootDir));
const compressDistZopfli = (rootDir, modernDir) => const compressDistZopfli = (rootDir, modernDir, compressModern = false) =>
gulp gulp
.src( .src(
[ [
`${rootDir}/**/${filesGlob}`, `${rootDir}/**/${filesGlob}`,
`!${modernDir}/**/${filesGlob}`, compressModern ? undefined : `!${modernDir}/**/${filesGlob}`,
`!${rootDir}/{sw-modern,service_worker}.js`, `!${rootDir}/{sw-modern,service_worker}.js`,
`${rootDir}/{authorize,onboarding}.html`, `${rootDir}/{authorize,onboarding}.html`,
], ].filter(Boolean),
{ base: rootDir } { base: rootDir }
) )
.pipe(zopfli(zopfliOptions)) .pipe(zopfli(zopfliOptions))
@ -40,12 +46,20 @@ const compressDistZopfli = (rootDir, modernDir) =>
const compressAppBrotli = () => const compressAppBrotli = () =>
compressDistBrotli(paths.app_output_root, paths.app_output_latest); compressDistBrotli(paths.app_output_root, paths.app_output_latest);
const compressHassioBrotli = () => const compressHassioBrotli = () =>
compressDistBrotli(paths.hassio_output_root, paths.hassio_output_latest); compressDistBrotli(
paths.hassio_output_root,
paths.hassio_output_latest,
false
);
const compressAppZopfli = () => const compressAppZopfli = () =>
compressDistZopfli(paths.app_output_root, paths.app_output_latest); compressDistZopfli(paths.app_output_root, paths.app_output_latest);
const compressHassioZopfli = () => const compressHassioZopfli = () =>
compressDistZopfli(paths.hassio_output_root, paths.hassio_output_latest); compressDistZopfli(
paths.hassio_output_root,
paths.hassio_output_latest,
true
);
gulp.task("compress-app", gulp.parallel(compressAppBrotli, compressAppZopfli)); gulp.task("compress-app", gulp.parallel(compressAppBrotli, compressAppZopfli));
gulp.task( gulp.task(

View File

@ -28,7 +28,7 @@
"@babel/runtime": "7.25.6", "@babel/runtime": "7.25.6",
"@braintree/sanitize-url": "7.1.0", "@braintree/sanitize-url": "7.1.0",
"@codemirror/autocomplete": "6.18.0", "@codemirror/autocomplete": "6.18.0",
"@codemirror/commands": "6.6.0", "@codemirror/commands": "6.6.1",
"@codemirror/language": "6.10.2", "@codemirror/language": "6.10.2",
"@codemirror/legacy-modes": "6.4.1", "@codemirror/legacy-modes": "6.4.1",
"@codemirror/search": "6.5.6", "@codemirror/search": "6.5.6",

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20240903.1" version = "20240904.0"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "The Home Assistant frontend" description = "The Home Assistant frontend"
readme = "README.md" readme = "README.md"

View File

@ -25,7 +25,6 @@ import { fireEvent } from "../../common/dom/fire_event";
import { stringCompare } from "../../common/string/compare"; import { stringCompare } from "../../common/string/compare";
import { debounce } from "../../common/util/debounce"; import { debounce } from "../../common/util/debounce";
import { groupBy } from "../../common/util/group-by"; import { groupBy } from "../../common/util/group-by";
import { nextRender } from "../../common/util/render-status";
import { haStyleScrollbar } from "../../resources/styles"; import { haStyleScrollbar } from "../../resources/styles";
import { loadVirtualizer } from "../../resources/virtualizer"; import { loadVirtualizer } from "../../resources/virtualizer";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
@ -35,6 +34,7 @@ import "../ha-svg-icon";
import "../search-input"; import "../search-input";
import { filterData, sortData } from "./sort-filter"; import { filterData, sortData } from "./sort-filter";
import { LocalizeFunc } from "../../common/translations/localize"; import { LocalizeFunc } from "../../common/translations/localize";
import { nextRender } from "../../common/util/render-status";
export interface RowClickedEvent { export interface RowClickedEvent {
id: string; id: string;
@ -169,8 +169,6 @@ export class HaDataTable extends LitElement {
@query("slot[name='header']") private _header!: HTMLSlotElement; @query("slot[name='header']") private _header!: HTMLSlotElement;
@state() private _items: DataTableRowData[] = [];
@state() private _collapsedGroups: string[] = []; @state() private _collapsedGroups: string[] = [];
private _checkableRowsCount?: number; private _checkableRowsCount?: number;
@ -179,7 +177,9 @@ export class HaDataTable extends LitElement {
private _sortColumns: SortableColumnContainer = {}; private _sortColumns: SortableColumnContainer = {};
private curRequest = 0; private _curRequest = 0;
private _lastUpdate = 0;
// @ts-ignore // @ts-ignore
@restoreScroll(".scroller") private _savedScrollPos?: number; @restoreScroll(".scroller") private _savedScrollPos?: number;
@ -206,9 +206,9 @@ export class HaDataTable extends LitElement {
public connectedCallback() { public connectedCallback() {
super.connectedCallback(); super.connectedCallback();
if (this._items.length) { if (this._filteredData.length) {
// Force update of location of rows // Force update of location of rows
this._items = [...this._items]; this._filteredData = [...this._filteredData];
} }
} }
@ -291,16 +291,13 @@ export class HaDataTable extends LitElement {
properties.has("columns") || properties.has("columns") ||
properties.has("_filter") || properties.has("_filter") ||
properties.has("sortColumn") || properties.has("sortColumn") ||
properties.has("sortDirection") || properties.has("sortDirection")
properties.has("groupColumn") ||
properties.has("groupOrder") ||
properties.has("_collapsedGroups")
) { ) {
this._sortFilterData(); this._sortFilterData();
} }
if (properties.has("selectable") || properties.has("hiddenColumns")) { if (properties.has("selectable") || properties.has("hiddenColumns")) {
this._items = [...this._items]; this._filteredData = [...this._filteredData];
} }
} }
@ -467,7 +464,15 @@ export class HaDataTable extends LitElement {
scroller scroller
class="mdc-data-table__content scroller ha-scrollbar" class="mdc-data-table__content scroller ha-scrollbar"
@scroll=${this._saveScrollPos} @scroll=${this._saveScrollPos}
.items=${this._items} .items=${this._groupData(
this._filteredData,
localize,
this.appendRow,
this.hasFab,
this.groupColumn,
this.groupOrder,
this._collapsedGroups
)}
.keyFunction=${this._keyFunction} .keyFunction=${this._keyFunction}
.renderItem=${renderRow} .renderItem=${renderRow}
></lit-virtualizer> ></lit-virtualizer>
@ -602,8 +607,13 @@ export class HaDataTable extends LitElement {
private async _sortFilterData() { private async _sortFilterData() {
const startTime = new Date().getTime(); const startTime = new Date().getTime();
this.curRequest++; const timeBetweenUpdate = startTime - this._lastUpdate;
const curRequest = this.curRequest; const timeBetweenRequest = startTime - this._curRequest;
this._curRequest = startTime;
const forceUpdate =
!this._lastUpdate ||
(timeBetweenUpdate > 500 && timeBetweenRequest < 500);
let filteredData = this.data; let filteredData = this.data;
if (this._filter) { if (this._filter) {
@ -614,6 +624,10 @@ export class HaDataTable extends LitElement {
); );
} }
if (!forceUpdate && this._curRequest !== startTime) {
return;
}
const prom = this.sortColumn const prom = this.sortColumn
? sortData( ? sortData(
filteredData, filteredData,
@ -634,91 +648,103 @@ export class HaDataTable extends LitElement {
setTimeout(resolve, 100 - elapsed); setTimeout(resolve, 100 - elapsed);
}); });
} }
if (this.curRequest !== curRequest) {
if (!forceUpdate && this._curRequest !== startTime) {
return; return;
} }
const localize = this.localizeFunc || this.hass.localize; this._lastUpdate = startTime;
if (this.appendRow || this.hasFab || this.groupColumn) {
let items = [...data];
if (this.groupColumn) {
const grouped = groupBy(items, (item) => item[this.groupColumn!]);
if (grouped.undefined) {
// make sure ungrouped items are at the bottom
grouped[UNDEFINED_GROUP_KEY] = grouped.undefined;
delete grouped.undefined;
}
const sorted: {
[key: string]: DataTableRowData[];
} = Object.keys(grouped)
.sort((a, b) => {
const orderA = this.groupOrder?.indexOf(a) ?? -1;
const orderB = this.groupOrder?.indexOf(b) ?? -1;
if (orderA !== orderB) {
if (orderA === -1) {
return 1;
}
if (orderB === -1) {
return -1;
}
return orderA - orderB;
}
return stringCompare(
["", "-", "—"].includes(a) ? "zzz" : a,
["", "-", "—"].includes(b) ? "zzz" : b,
this.hass.locale.language
);
})
.reduce((obj, key) => {
obj[key] = grouped[key];
return obj;
}, {});
const groupedItems: DataTableRowData[] = [];
Object.entries(sorted).forEach(([groupName, rows]) => {
groupedItems.push({
append: true,
content: html`<div
class="mdc-data-table__cell group-header"
role="cell"
.group=${groupName}
@click=${this._collapseGroup}
>
<ha-icon-button
.path=${mdiChevronUp}
class=${this._collapsedGroups.includes(groupName)
? "collapsed"
: ""}
>
</ha-icon-button>
${groupName === UNDEFINED_GROUP_KEY
? localize("ui.components.data-table.ungrouped")
: groupName || ""}
</div>`,
});
if (!this._collapsedGroups.includes(groupName)) {
groupedItems.push(...rows);
}
});
items = groupedItems;
}
if (this.appendRow) {
items.push({ append: true, content: this.appendRow });
}
if (this.hasFab) {
items.push({ empty: true });
}
this._items = items;
} else {
this._items = data;
}
this._filteredData = data; this._filteredData = data;
} }
private _groupData = memoizeOne(
(
data: DataTableRowData[],
localize: LocalizeFunc,
appendRow,
hasFab: boolean,
groupColumn: string | undefined,
groupOrder: string[] | undefined,
collapsedGroups: string[]
) => {
if (appendRow || hasFab || groupColumn) {
let items = [...data];
if (groupColumn) {
const grouped = groupBy(items, (item) => item[groupColumn]);
if (grouped.undefined) {
// make sure ungrouped items are at the bottom
grouped[UNDEFINED_GROUP_KEY] = grouped.undefined;
delete grouped.undefined;
}
const sorted: {
[key: string]: DataTableRowData[];
} = Object.keys(grouped)
.sort((a, b) => {
const orderA = groupOrder?.indexOf(a) ?? -1;
const orderB = groupOrder?.indexOf(b) ?? -1;
if (orderA !== orderB) {
if (orderA === -1) {
return 1;
}
if (orderB === -1) {
return -1;
}
return orderA - orderB;
}
return stringCompare(
["", "-", "—"].includes(a) ? "zzz" : a,
["", "-", "—"].includes(b) ? "zzz" : b,
this.hass.locale.language
);
})
.reduce((obj, key) => {
obj[key] = grouped[key];
return obj;
}, {});
const groupedItems: DataTableRowData[] = [];
Object.entries(sorted).forEach(([groupName, rows]) => {
groupedItems.push({
append: true,
content: html`<div
class="mdc-data-table__cell group-header"
role="cell"
.group=${groupName}
@click=${this._collapseGroup}
>
<ha-icon-button
.path=${mdiChevronUp}
class=${collapsedGroups.includes(groupName)
? "collapsed"
: ""}
>
</ha-icon-button>
${groupName === UNDEFINED_GROUP_KEY
? localize("ui.components.data-table.ungrouped")
: groupName || ""}
</div>`,
});
if (!collapsedGroups.includes(groupName)) {
groupedItems.push(...rows);
}
});
items = groupedItems;
}
if (appendRow) {
items.push({ append: true, content: appendRow });
}
if (hasFab) {
items.push({ empty: true });
}
return items;
}
return data;
}
);
private _memFilterData = memoizeOne( private _memFilterData = memoizeOne(
( (
data: DataTableRowData[], data: DataTableRowData[],
@ -802,8 +828,8 @@ export class HaDataTable extends LitElement {
private _checkedRowsChanged() { private _checkedRowsChanged() {
// force scroller to update, change it's items // force scroller to update, change it's items
if (this._items.length) { if (this._filteredData.length) {
this._items = [...this._items]; this._filteredData = [...this._filteredData];
} }
fireEvent(this, "selection-changed", { fireEvent(this, "selection-changed", {
value: this._checkedRows, value: this._checkedRows,

155
src/components/ha-badge.ts Normal file
View 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;
}
}

View File

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

View File

@ -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;
}
`,
];
} }
} }

View File

@ -1458,15 +1458,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@codemirror/commands@npm:6.6.0": "@codemirror/commands@npm:6.6.1":
version: 6.6.0 version: 6.6.1
resolution: "@codemirror/commands@npm:6.6.0" resolution: "@codemirror/commands@npm:6.6.1"
dependencies: dependencies:
"@codemirror/language": "npm:^6.0.0" "@codemirror/language": "npm:^6.0.0"
"@codemirror/state": "npm:^6.4.0" "@codemirror/state": "npm:^6.4.0"
"@codemirror/view": "npm:^6.27.0" "@codemirror/view": "npm:^6.27.0"
"@lezer/common": "npm:^1.1.0" "@lezer/common": "npm:^1.1.0"
checksum: 10/e984511b8ac06a1afe01005a50af7a0b6765cf350547d281a13aedbcccbc8620187c6dce55caa0f7c8ba409771e4abb4aa3bae824b6870249c8176b57cc4a42e checksum: 10/533f33db08a4ff7e400ff61e48d34525afdc894f92442a5bf469ed24b1992a61bf726b61647e2faa552a2f4703c149c92026d9a1f9169f615582cdfc36fcb010
languageName: node languageName: node
linkType: hard linkType: hard
@ -8922,7 +8922,7 @@ __metadata:
"@braintree/sanitize-url": "npm:7.1.0" "@braintree/sanitize-url": "npm:7.1.0"
"@bundle-stats/plugin-webpack-filter": "npm:4.15.0" "@bundle-stats/plugin-webpack-filter": "npm:4.15.0"
"@codemirror/autocomplete": "npm:6.18.0" "@codemirror/autocomplete": "npm:6.18.0"
"@codemirror/commands": "npm:6.6.0" "@codemirror/commands": "npm:6.6.1"
"@codemirror/language": "npm:6.10.2" "@codemirror/language": "npm:6.10.2"
"@codemirror/legacy-modes": "npm:6.4.1" "@codemirror/legacy-modes": "npm:6.4.1"
"@codemirror/search": "npm:6.5.6" "@codemirror/search": "npm:6.5.6"