1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-17 23:54:28 +01:00

Fix tile secondary info pop in (#51308)

* Add support for skeleton on tile info secondary text

* Show loading state for users of tile info

* Update src/components/tile/ha-tile-info.ts

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
This commit is contained in:
Aidan Timson
2026-03-31 15:38:03 +01:00
committed by GitHub
parent 2b28a6c3f2
commit 07b4a44228
4 changed files with 62 additions and 11 deletions

View File

@@ -1,3 +1,4 @@
import "@home-assistant/webawesome/dist/components/skeleton/skeleton";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
@@ -12,6 +13,8 @@ import { customElement, property } from "lit/decorators";
* @slot primary - The primary text container.
* @slot secondary - The secondary text container.
*
* @property {boolean} secondaryLoading - Whether the secondary text is loading. Shows a skeleton placeholder.
*
* @cssprop --ha-tile-info-primary-font-size - The font size of the primary text. defaults to `var(--ha-font-size-m)`.
* @cssprop --ha-tile-info-primary-font-weight - The font weight of the primary text. defaults to `var(--ha-font-weight-medium)`.
* @cssprop --ha-tile-info-primary-line-height - The line height of the primary text. defaults to `var(--ha-line-height-normal)`.
@@ -29,21 +32,31 @@ export class HaTileInfo extends LitElement {
@property() public secondary?: string;
@property({ type: Boolean, attribute: "secondary-loading" })
public secondaryLoading = false;
protected render() {
return html`
<div class="info">
<slot name="primary" class="primary">
<span>${this.primary}</span>
</slot>
<slot name="secondary" class="secondary">
<span>${this.secondary}</span>
</slot>
${this.secondaryLoading
? html`<div class="secondary">
<wa-skeleton class="placeholder" effect="pulse"></wa-skeleton>
</div>`
: html`<slot name="secondary" class="secondary">
<span>${this.secondary}</span>
</slot>`}
</div>
`;
}
static styles = css`
:host {
display: block;
width: 100%;
min-width: 0;
--tile-info-primary-font-size: var(
--ha-tile-info-primary-font-size,
var(--ha-font-size-m)
@@ -112,6 +125,15 @@ export class HaTileInfo extends LitElement {
line-height: var(--tile-info-secondary-line-height);
letter-spacing: var(--tile-info-secondary-letter-spacing);
color: var(--tile-info-secondary-color);
width: 100%;
}
.placeholder {
width: 140px;
max-width: 100%;
height: var(--tile-info-secondary-font-size);
--wa-border-radius-pill: var(--ha-border-radius-sm);
--color: var(--ha-color-fill-neutral-normal-resting);
--sheen-color: var(--ha-color-fill-neutral-loud-resting);
}
`;
}

View File

@@ -35,7 +35,7 @@ export class HuiDiscoveredDevicesCard
@state() private _config?: DiscoveredDevicesCardConfig;
@state() private _discoveredFlows: DataEntryFlowProgress[] = [];
@state() private _discoveredFlows?: DataEntryFlowProgress[];
public hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
if (!this.hass!.user?.is_admin) {
@@ -57,7 +57,10 @@ export class HuiDiscoveredDevicesCard
messages.forEach((message) => {
if (message.type === "removed") {
this._discoveredFlows = this._discoveredFlows.filter(
const flows = Array.isArray(this._discoveredFlows)
? this._discoveredFlows
: [];
this._discoveredFlows = flows.filter(
(flow) => flow.flow_id !== message.flow_id
);
return;
@@ -78,7 +81,10 @@ export class HuiDiscoveredDevicesCard
return;
}
const existingFlows = fullUpdate ? [] : this._discoveredFlows;
const existingFlows =
fullUpdate || !Array.isArray(this._discoveredFlows)
? []
: this._discoveredFlows;
this._discoveredFlows = [...existingFlows, ...newFlows];
}
),
@@ -138,7 +144,9 @@ export class HuiDiscoveredDevicesCard
// Update visibility based on admin status and discovered devices count
const shouldBeHidden = Boolean(
!this.hass.user?.is_admin ||
(this._config.hide_empty && this._discoveredFlows.length === 0)
(this._config.hide_empty &&
this._discoveredFlows &&
this._discoveredFlows.length === 0)
);
if (shouldBeHidden !== this.hidden) {
@@ -153,7 +161,9 @@ export class HuiDiscoveredDevicesCard
return nothing;
}
const count = this._discoveredFlows.length;
const count = Array.isArray(this._discoveredFlows)
? this._discoveredFlows.length
: 0;
const label = this.hass.localize("ui.card.discovered-devices.title");
const secondary =
@@ -162,6 +172,7 @@ export class HuiDiscoveredDevicesCard
count,
})
: this.hass.localize("ui.card.discovered-devices.no_devices");
const secondaryLoading = !this._discoveredFlows;
return html`
<ha-card>
@@ -179,6 +190,7 @@ export class HuiDiscoveredDevicesCard
slot="info"
.primary=${label}
.secondary=${secondary}
.secondaryLoading=${secondaryLoading}
></ha-tile-info>
</ha-tile-container>
</ha-card>

View File

@@ -3,6 +3,7 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { computeCssColor } from "../../../common/color/compute-color";
import { calcDate } from "../../../common/datetime/calc_date";
import { computeDomain } from "../../../common/entity/compute_domain";
@@ -115,6 +116,11 @@ export class HuiHomeSummaryCard
);
}
private _computeSecondaryLoading = memoizeOne(
(summary: HomeSummary, energyData: EnergyData | undefined): boolean =>
summary === "energy" && !energyData
);
private _computeSummaryState(): string {
if (!this._config || !this.hass) {
return "";
@@ -289,6 +295,10 @@ export class HuiHomeSummaryCard
};
const secondary = this._computeSummaryState();
const secondaryLoading = this._computeSecondaryLoading(
this._config.summary,
this._energyData
);
const label = getSummaryLabel(this.hass.localize, this._config.summary);
const icon = HOME_SUMMARIES_ICONS[this._config.summary];
@@ -309,6 +319,7 @@ export class HuiHomeSummaryCard
slot="info"
.primary=${label}
.secondary=${secondary}
.secondaryLoading=${secondaryLoading}
></ha-tile-info>
</ha-tile-container>
</ha-card>

View File

@@ -31,7 +31,7 @@ export class HuiRepairsCard
@state() private _config?: RepairsCardConfig;
@state() private _repairsIssues: RepairsIssue[] = [];
@state() private _repairsIssues?: RepairsIssue[];
public hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
return [
@@ -99,7 +99,9 @@ export class HuiRepairsCard
// Update visibility based on admin status and repairs count
const shouldBeHidden = Boolean(
!this.hass.user?.is_admin ||
(this._config.hide_empty && this._repairsIssues.length === 0)
(this._config.hide_empty &&
this._repairsIssues &&
this._repairsIssues.length === 0)
);
if (shouldBeHidden !== this.hidden) {
@@ -114,7 +116,9 @@ export class HuiRepairsCard
return nothing;
}
const count = this._repairsIssues.length;
const count = Array.isArray(this._repairsIssues)
? this._repairsIssues.length
: 0;
const label = this.hass.localize("ui.card.repairs.title");
const secondary =
@@ -123,6 +127,7 @@ export class HuiRepairsCard
count,
})
: this.hass.localize("ui.card.repairs.no_issues");
const secondaryLoading = !this._repairsIssues;
return html`
<ha-card>
@@ -140,6 +145,7 @@ export class HuiRepairsCard
slot="info"
.primary=${label}
.secondary=${secondary}
.secondaryLoading=${secondaryLoading}
></ha-tile-info>
</ha-tile-container>
</ha-card>