Align storage life time design with used storage (#26724)

This commit is contained in:
Paul Bottein
2025-08-27 09:52:56 +02:00
committed by GitHub
parent ddb224e145
commit 0b11302b1d
3 changed files with 119 additions and 51 deletions

View File

@@ -1,5 +1,5 @@
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import "./ha-tooltip";
@@ -7,7 +7,7 @@ import "./ha-tooltip";
export interface Segment {
value: number;
color: string;
label: TemplateResult | string;
label?: TemplateResult | string;
}
@customElement("ha-segmented-bar")
@@ -18,6 +18,12 @@ class HaSegmentedBar extends LitElement {
@property({ type: String }) public description?: string;
@property({ type: Boolean, attribute: "hide-legend" })
public hideLegend = false;
@property({ type: Boolean, attribute: "hide-tooltip" })
public hideTooltip = false;
protected render(): TemplateResult {
const totalValue = this.segments.reduce(
(acc, segment) => acc + segment.value,
@@ -26,27 +32,37 @@ class HaSegmentedBar extends LitElement {
return html`
<div class="container">
<div class="heading">
<div class="title">
<span>${this.heading}</span>
<span>${this.description}</span>
</div>
<slot name="extra"></slot>
</div>
<div class="bar">
${this.segments.map(
(segment) => html`
<ha-tooltip>
<span slot="content">${segment.label}</span>
<div
${this.segments.map((segment) => {
const bar = html`<div
style=${styleMap({
width: `${(segment.value / totalValue) * 100}%`,
backgroundColor: segment.color,
})}
></div>
></div>`;
return this.hideTooltip && !segment.label
? bar
: html`
<ha-tooltip>
<span slot="content">${segment.label}</span>
${bar}
</ha-tooltip>
`
)}
`;
})}
</div>
${this.hideLegend
? nothing
: html`
<ul class="legend">
${this.segments.map(
(segment) => html`
${this.segments.map((segment) =>
segment.label
? html`
<li>
<div
class="bullet"
@@ -57,8 +73,10 @@ class HaSegmentedBar extends LitElement {
<span class="label">${segment.label}</span>
</li>
`
: nothing
)}
</ul>
`}
</div>
`;
}
@@ -67,12 +85,20 @@ class HaSegmentedBar extends LitElement {
.container {
width: 100%;
}
.heading span {
.heading {
display: flex;
flex-direction: row;
gap: 8px;
}
.heading .title {
flex: 1;
}
.heading .title span {
color: var(--secondary-text-color);
line-height: var(--ha-line-height-expanded);
margin-right: 8px;
}
.heading span:first-child {
.heading .title span:first-child {
color: var(--primary-text-color);
}
.bar {
@@ -113,6 +139,9 @@ class HaSegmentedBar extends LitElement {
height: 12px;
border-radius: 50%;
}
.spacer {
flex: 1;
}
`;
}

View File

@@ -1,6 +1,7 @@
import {
mdiBackupRestore,
mdiFolder,
mdiInformation,
mdiNas,
mdiPlayBox,
mdiReload,
@@ -18,7 +19,6 @@ import "../../../components/ha-icon-button";
import "../../../components/ha-icon-next";
import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/ha-metric";
import "../../../components/ha-segmented-bar";
import "../../../components/ha-svg-icon";
import { extractApiErrorMessage } from "../../../data/hassio/common";
@@ -47,6 +47,7 @@ import { showMoveDatadiskDialog } from "./show-dialog-move-datadisk";
import { showMountViewDialog } from "./show-dialog-view-mount";
import type { Segment } from "../../../components/ha-segmented-bar";
import { getGraphColorByIndex } from "../../../common/color/colors";
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
@customElement("ha-config-section-storage")
class HaConfigSectionStorage extends LitElement {
@@ -107,21 +108,7 @@ class HaConfigSectionStorage extends LitElement {
this._hostInfo,
this._storageInfo
)}
${this._hostInfo.disk_life_time !== null
? // prettier-ignore
html`
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.storage.lifetime_used"
)}
.value=${this._hostInfo.disk_life_time}
.tooltip=${this.hass.localize(
"ui.panel.config.storage.lifetime_used_description"
)}
class="emmc"
></ha-metric>
`
: ""}
${this._renderDiskLifeTime(this._hostInfo.disk_life_time)}
</div>
${this._hostInfo
? html`<div class="card-actions">
@@ -237,6 +224,51 @@ class HaConfigSectionStorage extends LitElement {
`;
}
private _renderDiskLifeTime(diskLifeTime: number | null) {
if (diskLifeTime === null) {
return nothing;
}
const segments: Segment[] = [
{
color: "var(--primary-color)",
value: diskLifeTime,
},
{
color:
"var(--ha-bar-background-color, var(--secondary-background-color))",
value: 100 - diskLifeTime,
},
];
return html`
<ha-segmented-bar
.heading=${this.hass.localize("ui.panel.config.storage.lifetime")}
.description=${this.hass.localize(
"ui.panel.config.storage.lifetime_description",
{
lifetime: `${diskLifeTime}${blankBeforePercent(this.hass.locale)}%`,
}
)}
.segments=${segments}
hide-legend
hide-tooltip
>
<ha-tooltip slot="extra">
<ha-icon-button
.path=${mdiInformation}
class="help-button"
></ha-icon-button>
<p class="metric-description" slot="content">
${this.hass.localize(
"ui.panel.config.storage.lifetime_used_description"
)}
</p>
</ha-tooltip>
</ha-segmented-bar>
`;
}
private _renderStorageMetrics = memoizeOne(
(hostInfo?: HassioHostInfo, storageInfo?: HostDisksUsage | null) => {
if (!hostInfo) {
@@ -462,6 +494,12 @@ class HaConfigSectionStorage extends LitElement {
inset-inline-start: initial;
}
.help-button {
--mdc-icon-button-size: 20px;
--mdc-icon-size: 20px;
color: var(--secondary-text-color);
}
.no-mounts {
display: flex;
flex-direction: column;

View File

@@ -6678,7 +6678,8 @@
"homeassistant": "Home Assistant",
"ssl": "SSL"
},
"lifetime_used": "Lifetime used",
"lifetime": "Lifetime",
"lifetime_description": "{lifetime} used",
"lifetime_used_description": "The drives wear level is shown as a percentage, based on endurance indicators reported by the device via NVMe SMART or eMMC lifetime estimate fields.",
"disk_metrics": "Disk metrics",
"datadisk": {