Compare commits

...
Author SHA1 Message Date
Claude 0ec56d10c4 Migrate the remaining static lists from ha-list
Moves the auth provider picker, the config flow menu step, the
onboarding address search results, the input select options, the
system log card, the Wi-Fi access points, the network storage mounts
and the adjust sum statistics list from the mwc-based ha-list and
ha-list-item to ha-list-base, ha-list-selectable, ha-list-item-button,
ha-list-item-option and ha-list-item-base.

- Make the onboarding address results and the Wi-Fi access points
  single-select lists, as they were activatable lists before
- Copy the picked access point into a new Wi-Fi configuration instead
  of using the access point object itself, which the form then changed
- Let the config flow menu descriptions wrap, as multiline-secondary did
- Keep the list padding, icon colors and mount icon column of the
  previous lists
- Remove unused element imports in the touched files

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ln7qMagr2YDdssKkaBiqf5
2026-10-29 13:43:38 +00:00
8 changed files with 170 additions and 155 deletions

No files matched your search

+18 -12
View File
@@ -4,8 +4,8 @@ import { consumeLocalize } from "../common/decorators/consume-context-entry";
import { fireEvent } from "../common/dom/fire_event";
import type { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-icon-next";
import "../components/ha-list";
import "../components/ha-list-item";
import "../components/item/ha-list-item-button";
import "../components/list/ha-list-base";
import type { AuthProvider } from "../data/auth";
declare global {
@@ -32,21 +32,21 @@ export class HaPickAuthProvider extends LitElement {
>${this._localize("ui.panel.page-authorize.pick_auth_provider")}</span
>
</h3>
<ha-list>
<ha-list-base
aria-label=${this._localize("ui.panel.page-authorize.pick_auth_provider")}
>
${this.authProviders.map(
(provider) => html`
<ha-list-item
hasMeta
role="button"
<ha-list-item-button
.headline=${provider.name}
.auth_provider=${provider}
@click=${this._handlePick}
>
${provider.name}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
`
)}
</ha-list>
</ha-list-base>
`;
}
@@ -80,9 +80,15 @@ export class HaPickAuthProvider extends LitElement {
background: var(--card-background-color);
padding: 0 15px;
}
ha-list {
ha-list-base {
margin: 16px -16px 0;
--mdc-list-side-padding: 24px;
--ha-list-padding: var(--ha-space-2) 0;
}
ha-list-item-button {
--ha-row-item-padding-inline: var(--ha-space-6);
}
ha-icon-next {
color: var(--secondary-text-color);
}
`;
}
+19 -13
View File
@@ -3,7 +3,8 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-icon-next";
import "../../components/ha-list-item";
import "../../components/item/ha-list-item-button";
import "../../components/list/ha-list-base";
import type { DataEntryFlowStepMenu } from "../../data/data_entry_flow";
import type { HomeAssistant } from "../../types";
import type { FlowConfig } from "./show-dialog-data-entry-flow";
@@ -79,28 +80,27 @@ class StepFlowMenu extends LitElement {
return html`
${description ? html`<div class="content">${description}</div>` : nothing}
<div class="options">
<ha-list-base class="options">
${options.map((option) => {
const optionDescription = optionDescriptions[option];
return html`
<ha-list-item
hasMeta
<ha-list-item-button
.headline=${translations[option]}
.step=${option}
@click=${this._handleStep}
?twoline=${optionDescription}
?multiline-secondary=${optionDescription}
>
<span>${translations[option]}</span>
${
optionDescription
? html`<span slot="secondary"> ${optionDescription} </span>`
? html`<span slot="supporting-text" class="description"
>${optionDescription}</span
>`
: nothing
}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
`;
})}
</div>
</ha-list-base>
`;
}
@@ -129,8 +129,14 @@ class StepFlowMenu extends LitElement {
.content + .options {
margin-top: var(--ha-space-2);
}
ha-list-item {
--mdc-list-side-padding: var(--ha-space-6);
ha-list-item-button {
--ha-row-item-padding-inline: var(--ha-space-6);
}
.description {
white-space: normal;
}
ha-icon-next {
color: var(--secondary-text-color);
}
`,
];
+31 -34
View File
@@ -18,8 +18,10 @@ import {
import type { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-alert";
import "../components/ha-button";
import "../components/ha-list";
import "../components/ha-list-item";
import "../components/item/ha-list-item-base";
import "../components/item/ha-list-item-option";
import "../components/list/ha-list-base";
import "../components/list/ha-list-selectable";
import "../components/ha-spinner";
import "../components/input/ha-input";
import type { HaInput } from "../components/input/ha-input";
@@ -141,10 +143,12 @@ class OnboardingLocation extends LitElement {
${
this._places !== undefined
? html`
<ha-list activatable>
${
this._places?.length
? this._places.map((place) => {
${
this._places?.length
? html`<ha-list-selectable
@ha-list-item-selected=${this._placeSelected}
>
${this._places.map((place) => {
const primary = [
place.name || place.address[place.category],
place.address.house_number,
@@ -164,26 +168,19 @@ class OnboardingLocation extends LitElement {
]
.filter(Boolean)
.join(", ");
return html`<ha-list-item
@click=${this._itemClicked}
.placeId=${place.place_id}
return html`<ha-list-item-option
.selected=${this._highlightedMarker === place.place_id}
.activated=${this._highlightedMarker === place.place_id}
.twoline=${primary && secondary}
>
${primary || secondary}
<span slot="secondary"
>${primary ? secondary : ""}</span
>
</ha-list-item>`;
})
: html`<ha-list-item noninteractive
>${
this._places === null ? "" : "No results"
}</ha-list-item
>`
}
</ha-list>
.headline=${primary || secondary}
.supportingText=${primary ? secondary : undefined}
></ha-list-item-option>`;
})}
</ha-list-selectable>`
: html`<ha-list-base>
<ha-list-item-base
.headline=${this._places === null ? "" : "No results"}
></ha-list-item-base>
</ha-list-base>`
}
`
: nothing
}
@@ -309,11 +306,9 @@ class OnboardingLocation extends LitElement {
this._country = place.address.country_code.toUpperCase();
}
private _itemClicked(ev) {
this._highlightedMarker = ev.currentTarget.placeId;
const place = this._places!.find(
(plc) => plc.place_id === ev.currentTarget.placeId
)!;
private _placeSelected(ev: CustomEvent<number>) {
const place = this._places![ev.detail];
this._highlightedMarker = place.place_id;
this._location = [Number(place.lat), Number(place.lon)];
this._country = place.address.country_code.toUpperCase();
}
@@ -505,17 +500,19 @@ class OnboardingLocation extends LitElement {
border-radius: var(--mdc-shape-large, 16px);
overflow: hidden;
}
ha-list {
ha-list-selectable,
ha-list-base {
display: block;
width: 100%;
border: 1px solid var(--divider-color);
box-sizing: border-box;
border-top-width: 0;
border-bottom-left-radius: var(--mdc-shape-small, 4px);
border-bottom-right-radius: var(--mdc-shape-small, 4px);
--mdc-list-vertical-padding: 0;
}
ha-list-item {
height: 72px;
ha-list-item-option,
ha-list-item-base {
--ha-row-item-min-height: 72px;
}
.attribution {
/* input helper style */
@@ -7,11 +7,11 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-button";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-icon-picker";
import "../../../../components/ha-list";
import "../../../../components/ha-list-item";
import "../../../../components/ha-sortable";
import "../../../../components/ha-svg-icon";
import "../../../../components/input/ha-input";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/list/ha-list-base";
import type { HaInput } from "../../../../components/input/ha-input";
import type { InputSelect } from "../../../../data/input_select";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
@@ -108,24 +108,26 @@ class HaInputSelectForm extends LitElement {
handle-selector=".handle"
.disabled=${this.disabled}
>
<ha-list class="options">
<ha-list-base
class="options"
aria-label=${this.hass!.localize(
"ui.dialogs.helper_settings.input_select.options"
)}
>
${
this._options.length
? repeat(
this._options,
(option) => option,
(option, index) => html`
<ha-list-item class="option" hasMeta>
<div class="optioncontent">
<div class="handle">
<ha-svg-icon
.path=${mdiDragHorizontalVariant}
></ha-svg-icon>
</div>
${option}
</div>
<ha-list-item-base class="option" .headline=${option}>
<ha-svg-icon
slot="start"
class="handle"
.path=${mdiDragHorizontalVariant}
></ha-svg-icon>
<ha-icon-button
slot="meta"
slot="end"
.index=${index}
.label=${this.hass.localize(
"ui.dialogs.helper_settings.input_select.remove_option"
@@ -134,12 +136,12 @@ class HaInputSelectForm extends LitElement {
.disabled=${this.disabled}
.path=${mdiDelete}
></ha-icon-button>
</ha-list-item>
</ha-list-item-base>
`
)
: nothing
}
</ha-list>
</ha-list-base>
</ha-sortable>
<div class="layout horizontal center">
<ha-input
@@ -236,13 +238,18 @@ class HaInputSelectForm extends LitElement {
.form {
color: var(--primary-text-color);
}
.options {
--ha-list-padding: var(--ha-space-2) 0;
}
.option ha-icon-button {
color: var(--secondary-text-color);
}
.option {
border: 1px solid var(--divider-color);
border-radius: var(--ha-border-radius-sm);
margin-top: var(--ha-space-1);
--ha-icon-button-size: 24px;
--mdc-ripple-color: transparent;
--mdc-list-side-padding: var(--ha-space-4);
--ha-row-item-gap: var(--ha-space-3);
cursor: default;
background-color: var(--card-background-color);
}
@@ -258,17 +265,6 @@ class HaInputSelectForm extends LitElement {
.handle {
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
padding-right: var(--ha-space-3);
padding-inline-end: var(--ha-space-3);
padding-inline-start: initial;
}
.handle ha-svg-icon {
pointer-events: none;
height: 24px;
}
.optioncontent {
display: flex;
align-items: center;
}
ha-icon-picker {
display: block;
+13 -10
View File
@@ -12,9 +12,9 @@ import "../../../components/ha-card";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/ha-spinner";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-base";
import { getSignedPath } from "../../../data/auth";
import {
getCoreLogFileDownloadUnavailableReason,
@@ -227,16 +227,18 @@ export class SystemLogCard extends LitElement {
)
}
</div>`
: html`<ha-list
: html`<ha-list-base
>${filteredItems.map(
(item, idx) => html`
<ha-list-item
<ha-list-item-button
@click=${this._openLog}
.logItem=${item}
twoline
.headline=${item.message[0]}
>
${item.message[0]}
<span slot="secondary" class="secondary">
<span
slot="supporting-text"
class="secondary"
>
${this._timestamp(item)} –
${html`(<span class=${item.level}
>${this.hass.localize(
@@ -265,9 +267,9 @@ export class SystemLogCard extends LitElement {
: nothing
}
</span>
</ha-list-item>
</ha-list-item-button>
`
)}</ha-list
)}</ha-list-base
>`
}
@@ -338,8 +340,9 @@ export class SystemLogCard extends LitElement {
:host {
direction: var(--direction);
}
ha-list {
ha-list-base {
direction: ltr;
--ha-list-padding: var(--ha-space-2) 0;
}
.header {
+21 -17
View File
@@ -14,15 +14,14 @@ import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-expansion-panel";
import "../../../components/ha-icon-button";
import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/radio/ha-radio-group";
import type { HaRadioGroup } from "../../../components/radio/ha-radio-group";
import "../../../components/radio/ha-radio-option";
import "../../../components/ha-spinner";
import "../../../components/ha-tab-group";
import "../../../components/ha-tab-group-tab";
import "../../../components/input/ha-input";
import "../../../components/item/ha-list-item-option";
import "../../../components/list/ha-list-selectable";
import type { HaInput } from "../../../components/input/ha-input";
import { extractApiErrorMessage } from "../../../data/hassio/common";
import {
@@ -169,29 +168,28 @@ export class HassioNetwork extends LitElement {
${
this._accessPoints.length
? html`
<ha-list>
<ha-list-selectable
@ha-list-item-selected=${this._selectAP}
>
${this._accessPoints.map(
(ap) => html`
<ha-list-item
twoline
@click=${this._selectAP}
.activated=${
<ha-list-item-option
.headline=${ap.ssid}
.selected=${
ap.ssid === this._wifiConfiguration?.ssid
}
.ap=${ap}
>
<span>${ap.ssid}</span>
<span slot="secondary">
<span slot="supporting-text">
${ap.mac} -
${this.hass.localize(
"ui.panel.config.network.supervisor.signal_strength"
)}:
${ap.signal}
</span>
</ha-list-item>
</ha-list-item-option>
`
)}
</ha-list>
</ha-list-selectable>
`
: nothing
}
@@ -274,8 +272,14 @@ export class HassioNetwork extends LitElement {
</div>`;
}
private _selectAP(event) {
this._wifiConfiguration = event.currentTarget.ap;
private _selectAP(ev: CustomEvent<number>) {
const ap = this._accessPoints[ev.detail];
this._wifiConfiguration = {
mode: ap.mode,
ssid: ap.ssid,
signal: ap.signal,
auth: "open",
};
IP_VERSIONS.forEach((version) => {
if (this._interface![version]!.method === "disabled") {
this._interface![version]!.method = "auto";
@@ -844,8 +848,8 @@ export class HassioNetwork extends LitElement {
.add-nameserver {
margin-top: 16px;
}
ha-list-item {
--mdc-list-side-padding: 10px;
ha-list-item-option {
--ha-row-item-padding-inline: 10px;
}
.card-actions {
display: flex;
@@ -18,12 +18,12 @@ import "../../../components/ha-bar";
import "../../../components/ha-button";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-next";
import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/ha-segmented-bar";
import type { Segment } from "../../../components/ha-segmented-bar";
import "../../../components/ha-spinner";
import "../../../components/ha-svg-icon";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-base";
import { extractApiErrorMessage } from "../../../data/hassio/common";
import type { HassioHostInfo, HostDisksUsage } from "../../../data/hassio/host";
import {
@@ -187,18 +187,18 @@ class HaConfigSectionStorage extends LitElement {
}
</ha-alert>`
: validMounts?.length
? html`<ha-list>
? html`<ha-list-base
aria-label=${this.hass.localize(
"ui.panel.config.storage.network_mounts.title"
)}
>
${validMounts.map(
(mount) => html`
<ha-list-item
graphic="avatar"
<ha-list-item-button
.mount=${mount}
twoline
multiline-secondary
hasMeta
@click=${this._changeMount}
>
<div slot="graphic">
<div slot="start" class="mount-icon">
<ha-svg-icon
.path=${
mount.usage === SupervisorMountUsage.MEDIA
@@ -210,11 +210,12 @@ class HaConfigSectionStorage extends LitElement {
></ha-svg-icon>
</div>
<span
slot="headline"
class="mount-state-${mount.state || "unknown"}"
>
${mount.name}
</span>
<span slot="secondary">
<span slot="supporting-text">
<span class="mount-address">
${supervisorMountDescription(mount)}
</span>
@@ -224,19 +225,17 @@ class HaConfigSectionStorage extends LitElement {
mount.state !== SupervisorMountState.ACTIVE
? html`<ha-icon-button
class="reload-btn"
slot="meta"
slot="end"
.mount=${mount}
@click=${this._reloadMount}
.path=${mdiReload}
></ha-icon-button>`
: html`<ha-icon-next
slot="meta"
></ha-icon-next>`
: html`<ha-icon-next slot="end"></ha-icon-next>`
}
</ha-list-item>
</ha-list-item-button>
`
)}
</ha-list>`
</ha-list-base>`
: html`<div class="no-mounts">
<ha-svg-icon .path=${mdiNas}></ha-svg-icon>
<p>
@@ -490,8 +489,6 @@ class HaConfigSectionStorage extends LitElement {
color: var(--warning-color);
}
/* multiline-secondary lets the secondary slot wrap, so the address keeps its
own single ellipsized line and only the usage sits below it. */
.mount-address {
display: block;
overflow: hidden;
@@ -560,14 +557,20 @@ class HaConfigSectionStorage extends LitElement {
border-radius: var(--ha-border-radius-circle);
margin-bottom: 8px;
}
ha-list-item {
--mdc-list-item-meta-size: auto;
--mdc-list-item-meta-display: flex;
}
ha-svg-icon,
ha-icon-next {
width: 24px;
}
.mount-icon {
display: flex;
justify-content: center;
width: 40px;
}
.mount-icon,
.reload-btn,
ha-list-item-button ha-icon-next {
color: var(--secondary-text-color);
}
ha-alert {
--ha-alert-icon-size: 24px;
@@ -10,12 +10,11 @@ import "../../../../components/ha-spinner";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-dialog";
import "../../../../components/ha-button";
import "../../../../components/ha-form/ha-form";
import "../../../../components/ha-icon-next";
import "../../../../components/ha-list-item";
import "../../../../components/ha-selector/ha-selector-datetime";
import "../../../../components/ha-selector/ha-selector-number";
import "../../../../components/ha-svg-icon";
import "../../../../components/item/ha-list-item-button";
import "../../../../components/list/ha-list-base";
import type { StatisticValue } from "../../../../data/recorder";
import {
adjustStatisticsSum,
@@ -241,25 +240,21 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
}
);
rows.push(html`
<ha-list-item
twoline
hasMeta
<ha-list-item-button
.headline=${change}
.supportingText=${formatDateTime(
new Date(stat.start),
this.hass.locale,
this.hass.config
)}
.stat=${stat}
@click=${this._setChosenStatistic}
>
<span>${change}</span>
<span slot="secondary">
${formatDateTime(
new Date(stat.start),
this.hass.locale,
this.hass.config
)}
</span>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
`);
}
stats = html`${rows}`;
stats = html`<ha-list-base>${rows}</ha-list-base>`;
}
return html`
@@ -635,9 +630,14 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends DirtyStateProvid
ha-selector-number {
margin-bottom: var(--ha-space-5);
}
ha-list-item {
ha-list-base {
margin: 0 -24px;
--mdc-list-side-padding: 24px;
}
ha-list-item-button {
--ha-row-item-padding-inline: var(--ha-space-6);
}
ha-list-item-button ha-icon-next {
color: var(--secondary-text-color);
}
.table-row {
display: flex;