Compare commits

..
Author SHA1 Message Date
Bram KragtenandClaude Opus 5 00f2c71231 Keep the entity name shape out of the core chunk
recorder.ts sits in the core entry chunk, so importing DEFAULT_ENTITY_NAME
from compute_entity_name_display dragged the registry-aware formatters and
their dependencies in with it and blew the core bundle budget by 4.1%.

The constant and the name types have no runtime dependencies, so they move
to their own module; compute_entity_name_display re-exports them so nothing
else has to change.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-15 15:41:43 +02:00
Bram KragtenandClaude Opus 5 66820826fa Resolve statistic labels with structured entity names
getStatisticLabel resolved names through computeStateName, so every
statistic-shaped label — the energy dashboard cards, the sankey cards,
hui-statistic-card, chart series names and the energy configuration
dialogs — showed the flat friendly_name attribute while the rest of the
UI composed names from the device, area and floor.

It now asks hass.formatEntityName for the default composition, which
also makes the entity branch of computeEnergyLabel redundant.

The metadata fallback stays: external statistics have no entity to
resolve a name against.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-15 15:21:43 +02:00
50 changed files with 1169 additions and 891 deletions
+1 -1
View File
@@ -106,7 +106,7 @@
"hls.js": "1.7.3",
"home-assistant-js-websocket": "9.6.0",
"idb-keyval": "6.3.0",
"intl-messageformat": "11.2.15",
"intl-messageformat": "11.2.14",
"js-yaml": "5.4.1",
"leaflet": "1.9.4",
"leaflet.markercluster": "1.5.3",
-6
View File
@@ -1,7 +1 @@
export const stopPropagation = (ev) => ev.stopPropagation();
export const stopKeydownEnterSpacePropagation = (ev: KeyboardEvent) => {
if (ev.key === "Enter" || ev.key === " " || ev.key === "Spacebar") {
ev.stopPropagation();
}
};
@@ -19,37 +19,16 @@ import {
getEntityContext,
getEntityEntryContext,
} from "./context/get_entity_context";
import type { EntityNameItem, EntityNameOptions } from "./entity_name_config";
const DEFAULT_SEPARATOR = " ";
export const DEFAULT_ENTITY_NAME = [
{ type: "parent_device" },
{ type: "device" },
{ type: "entity" },
] satisfies EntityNameItem[];
export const ENTITY_NAME_TYPES = [
"floor",
"area",
"parent_device",
"device",
"entity",
] as const;
export type EntityNameType = (typeof ENTITY_NAME_TYPES)[number];
export type EntityNameItem =
| {
type: EntityNameType;
}
| {
type: "text";
text: string;
};
export interface EntityNameOptions {
separator?: string;
}
export { DEFAULT_ENTITY_NAME, ENTITY_NAME_TYPES } from "./entity_name_config";
export type {
EntityNameItem,
EntityNameOptions,
EntityNameType,
} from "./entity_name_config";
// Joins items that need no entity context. Returns undefined as soon as one
// item references the registries (device, area, ...).
+34
View File
@@ -0,0 +1,34 @@
/**
* Shape of a composed entity name, kept free of runtime dependencies so that
* modules in the core entry chunk can name an entity without pulling in the
* registry-aware formatters from `compute_entity_name_display`.
*/
export const ENTITY_NAME_TYPES = [
"floor",
"area",
"parent_device",
"device",
"entity",
] as const;
export type EntityNameType = (typeof ENTITY_NAME_TYPES)[number];
export type EntityNameItem =
| {
type: EntityNameType;
}
| {
type: "text";
text: string;
};
export interface EntityNameOptions {
separator?: string;
}
export const DEFAULT_ENTITY_NAME = [
{ type: "parent_device" },
{ type: "device" },
{ type: "entity" },
] satisfies EntityNameItem[];
-7
View File
@@ -21,7 +21,6 @@ import {
} from "../common/datetime/calc_date";
import type { DateRange } from "../common/datetime/calc_date_range";
import { calcDateRange } from "../common/datetime/calc_date_range";
import { DEFAULT_ENTITY_NAME } from "../common/entity/compute_entity_name_display";
import { formatNumber } from "../common/number/format_number";
import { normalizeValueBySIPrefix } from "../common/number/normalize-by-si-prefix";
import { groupBy } from "../common/util/group-by";
@@ -328,12 +327,6 @@ export const computeEnergyLabel = (
return customName;
}
const stateObj = hass.states[statisticId];
if (stateObj) {
return hass.formatEntityName(stateObj, DEFAULT_ENTITY_NAME);
}
return getStatisticLabel(hass, statisticId, statisticsMetaData);
};
+3 -2
View File
@@ -1,5 +1,5 @@
import type { Connection } from "home-assistant-js-websocket";
import { computeStateName } from "../common/entity/compute_state_name";
import { DEFAULT_ENTITY_NAME } from "../common/entity/entity_name_config";
import type { HaDurationData } from "../components/ha-duration-input";
import type { HomeAssistant } from "../types";
import { firstWeekday } from "../common/datetime/first_weekday";
@@ -348,8 +348,9 @@ export const getStatisticLabel = (
): string => {
const entity = hass.states[statisticsId];
if (entity) {
return computeStateName(entity);
return hass.formatEntityName(entity, DEFAULT_ENTITY_NAME);
}
// External statistics have no entity to resolve a name against.
return statisticsMetaData?.name || statisticsId;
};
@@ -9,9 +9,9 @@ import {
mdiVideo,
mdiWebhook,
} from "@mdi/js";
import type { TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { TemplateResult } from "lit";
import { formatDate } from "../../../../common/datetime/format_date";
import { relativeTime } from "../../../../common/datetime/relative_time";
import { fireEvent } from "../../../../common/dom/fire_event";
@@ -20,13 +20,11 @@ import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-icon-next";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-svg-icon";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/item/ha-list-item-button";
import "../../../../components/list/ha-list-base";
import "../../../../components/list/ha-list-nav";
import type { BackupConfig } from "../../../../data/backup";
import { cloudBackupEnabled, cloudBackupHealth } from "../../../../data/backup";
import { cloudBackupHealth, cloudBackupEnabled } from "../../../../data/backup";
import type {
CloudStatusLoggedIn,
SubscriptionInfo,
@@ -262,8 +260,8 @@ export class CloudAccountOverview extends LitElement {
/>
</div>
<div class="card-content">
<ha-list-base>
<ha-list-item-base>
<ha-md-list>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.email"
@@ -285,16 +283,16 @@ export class CloudAccountOverview extends LitElement {
@click=${this._toggleEmail}
></ha-icon-button>
</span>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.subscription"
)}</span
>
<span slot="supporting-text">${this._subscriptionDetail()}</span>
</ha-list-item-base>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
</div>
<div class="card-actions split">
<ha-button
@@ -350,11 +348,7 @@ export class CloudAccountOverview extends LitElement {
"ui.panel.config.cloud.account.overview.features_intro"
)}
</p>
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.cloud.account.overview.features_title"
)}
>
<ha-md-list>
${this._featureRow(
mdiEarth,
this.hass.localize(
@@ -417,7 +411,7 @@ export class CloudAccountOverview extends LitElement {
webhookStatus,
"/config/cloud/webhooks"
)}
</ha-list-nav>
</ha-md-list>
</div>
</ha-card>
`;
@@ -430,12 +424,12 @@ export class CloudAccountOverview extends LitElement {
href: string
): TemplateResult {
return html`
<ha-list-item-button href=${href}>
<ha-md-list-item type="link" href=${href}>
<ha-svg-icon slot="start" .path=${icon}></ha-svg-icon>
<span slot="headline">${title}</span>
<span slot="supporting-text">${supporting}</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
`;
}
@@ -667,6 +661,9 @@ export class CloudAccountOverview extends LitElement {
width: 36px;
height: auto;
}
ha-md-list-item {
--md-item-overflow: visible;
}
.muted {
color: var(--secondary-text-color);
}
@@ -695,8 +692,10 @@ export class CloudAccountOverview extends LitElement {
.status-dot.disabled {
background-color: var(--error-color);
}
ha-list-base {
--ha-row-item-padding-inline: 0;
ha-md-list {
padding: 0;
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
}
.extras-header {
display: flex;
@@ -722,12 +721,14 @@ export class CloudAccountOverview extends LitElement {
padding-inline: var(--ha-space-4);
margin-top: 0;
}
.extras-content ha-list-nav {
.extras-content ha-md-list {
padding-top: 0;
--ha-row-item-padding-inline: var(--ha-space-4);
--md-list-item-leading-space: var(--ha-space-4);
--md-list-item-trailing-space: var(--ha-space-4);
}
.extras-content ha-list-item-button {
--ha-row-item-padding-block: var(--ha-space-2);
.extras-content ha-md-list-item {
--md-list-item-top-space: var(--ha-space-2);
--md-list-item-bottom-space: var(--ha-space-2);
}
.extras-content ha-svg-icon[slot="start"],
.extras-content ha-icon-next {
@@ -11,9 +11,9 @@ import { relativeTime } from "../../../../common/datetime/relative_time";
import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-spinner";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-svg-icon";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/list/ha-list-base";
import type { BackupConfig, BackupInfo } from "../../../../data/backup";
import {
CLOUD_AGENT,
@@ -137,8 +137,8 @@ export class CloudBackupPref extends LitElement {
)}
>
<div class="card-content">
<ha-list-base>
<ha-list-item-base>
<ha-md-list>
<ha-md-list-item>
<ha-svg-icon slot="start" .path=${mdiBackupRestore}></ha-svg-icon>
<span slot="headline">
${
@@ -159,11 +159,11 @@ export class CloudBackupPref extends LitElement {
)
}
</span>
</ha-list-item-base>
</ha-md-list-item>
${
cloudAgent
? html`
<ha-list-item-base>
<ha-md-list-item>
<ha-svg-icon
slot="start"
.path=${mdiHarddisk}
@@ -171,8 +171,8 @@ export class CloudBackupPref extends LitElement {
<span slot="headline">
${bytesToString(cloudAgent.size)}
</span>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<ha-svg-icon
slot="start"
.path=${mdiShieldLock}
@@ -188,11 +188,11 @@ export class CloudBackupPref extends LitElement {
)
}
</span>
</ha-list-item-base>
</ha-md-list-item>
`
: nothing
}
<ha-list-item-base>
<ha-md-list-item>
<ha-svg-icon slot="start" .path=${mdiCalendar}></ha-svg-icon>
<span slot="headline">
${
@@ -213,8 +213,8 @@ export class CloudBackupPref extends LitElement {
)
}
</span>
</ha-list-item-base>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
</div>
<div class="card-actions">
<ha-button appearance="filled" href="/config/backup?historyBack=1">
@@ -308,12 +308,15 @@ export class CloudBackupPref extends LitElement {
display: flex;
justify-content: flex-end;
}
ha-list-base {
--ha-row-item-padding-inline: 0;
ha-md-list {
background: none;
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
}
ha-list-item-base {
--ha-row-item-padding-block: var(--ha-space-2);
--ha-row-item-min-height: 40px;
ha-md-list-item {
--md-list-item-top-space: var(--ha-space-2);
--md-list-item-bottom-space: var(--ha-space-2);
--md-list-item-one-line-container-height: 40px;
}
ha-svg-icon[slot="start"] {
color: var(--secondary-text-color);
@@ -4,10 +4,10 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-switch";
import "../../../../components/ha-tip";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/list/ha-list-base";
import { formatDate } from "../../../../common/datetime/format_date";
import type { HaSwitch } from "../../../../components/ha-switch";
@@ -179,8 +179,8 @@ export class CloudRemotePref extends LitElement {
)}
>
<div class="card-content">
<ha-list-base>
<ha-list-item-base>
<ha-md-list>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.external_activation"
@@ -196,8 +196,8 @@ export class CloudRemotePref extends LitElement {
.checked=${remote_allow_remote_enable}
@change=${this._toggleAllowRemoteEnabledChanged}
></ha-switch>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.certificate_info"
@@ -232,8 +232,8 @@ export class CloudRemotePref extends LitElement {
"ui.panel.config.cloud.account.remote.more_info"
)}
</ha-button>
</ha-list-item-base>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
<ha-tip .hass=${this.hass}>
@@ -318,8 +318,13 @@ export class CloudRemotePref extends LitElement {
display: flex;
justify-content: flex-end;
}
ha-list-base {
--ha-row-item-padding-inline: 0;
ha-md-list {
background: none;
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
ha-tip {
max-width: 600px;
@@ -5,26 +5,26 @@ import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { storage } from "../../../../common/decorators/storage";
import { fireEvent } from "../../../../common/dom/fire_event";
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
import { computeStateDomain } from "../../../../common/entity/compute_state_domain";
import { computeStateName } from "../../../../common/entity/compute_state_name";
import { supportsFeature } from "../../../../common/entity/supports-feature";
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-language-picker";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-select";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-textarea";
import "../../../../components/ha-tip";
import "../../../../components/voice-assistant-brand-icon";
import type {
HaSelectOption,
HaSelectSelectEvent,
} from "../../../../components/ha-select";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-textarea";
import type { HaTextArea } from "../../../../components/ha-textarea";
import "../../../../components/ha-tip";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/list/ha-list-base";
import "../../../../components/voice-assistant-brand-icon";
import { showAutomationEditor } from "../../../../data/automation";
import type { CloudStatusLoggedIn } from "../../../../data/cloud";
import { updateCloudPref } from "../../../../data/cloud";
@@ -153,8 +153,8 @@ export class CloudTTSPref extends LitElement {
)}
>
<div class="card-content">
<ha-list-base>
<ha-list-item-base>
<ha-md-list>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.cloud.account.tts.default_language"
@@ -176,8 +176,8 @@ export class CloudTTSPref extends LitElement {
@value-changed=${this._handleLanguageChange}
>
</ha-language-picker>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.cloud.account.tts.default_voice"
@@ -199,8 +199,8 @@ export class CloudTTSPref extends LitElement {
}))}
>
</ha-select>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.components.media-browser.tts.selected_voice_id"
@@ -215,8 +215,8 @@ export class CloudTTSPref extends LitElement {
)}
@click=${this._copyVoiceId}
></ha-icon-button>
</ha-list-item-base>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
<div class="try-tts">
<span class="try-heading">
${this.hass.localize(
@@ -541,8 +541,13 @@ export class CloudTTSPref extends LitElement {
gap: var(--ha-space-2);
flex-wrap: wrap;
}
ha-list-base {
--ha-row-item-padding-inline: var(--ha-space-4);
ha-md-list {
background: none;
--md-list-item-leading-space: var(--ha-space-4);
--md-list-item-trailing-space: var(--ha-space-4);
}
ha-md-list-item {
--md-item-overflow: visible;
}
ha-language-picker,
ha-select {
@@ -1,5 +1,4 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import { consume } from "@lit/context";
import { mdiContentCopy, mdiRestore } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import type { CSSResultGroup, PropertyValues } from "lit";
@@ -7,6 +6,7 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import memoizeOne from "memoize-one";
import { consume } from "@lit/context";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { computeDomain } from "../../../common/entity/compute_domain";
import { computeObjectId } from "../../../common/entity/compute_object_id";
@@ -19,24 +19,23 @@ import type {
LocalizeKeys,
} from "../../../common/translations/localize";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
import "../../../components/entity/ha-entity-picker";
import "../../../components/ha-alert";
import "../../../components/ha-area-picker";
import "../../../components/ha-color-picker";
import "../../../components/ha-dropdown-item";
import "../../../components/entity/ha-entity-picker";
import "../../../components/ha-icon";
import "../../../components/ha-icon-button-next";
import "../../../components/ha-icon-picker";
import "../../../components/ha-labels-picker";
import "../../../components/ha-list-item";
import "../../../components/ha-md-list-item";
import "../../../components/ha-select";
import type { HaSelectSelectEvent } from "../../../components/ha-select";
import "../../../components/ha-state-icon";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/input/ha-input";
import "../../../components/item/ha-list-item-button";
import "../../../components/item/ha-row-item";
import "../../../components/list/ha-list-base";
import {
CAMERA_ORIENTATIONS,
CameraEntityFeature,
@@ -46,16 +45,16 @@ import {
STREAM_TYPE_HLS,
updateCameraPrefs,
} from "../../../data/camera";
import {
dirtyStateContext,
type DirtyStateContext,
} from "../../../data/context/dirty-state";
import type { ConfigEntry } from "../../../data/config_entries";
import { deleteConfigEntry } from "../../../data/config_entries";
import {
createConfigFlow,
handleConfigFlowStep,
} from "../../../data/config_flow";
import {
dirtyStateContext,
type DirtyStateContext,
} from "../../../data/context/dirty-state";
import type { DataEntryFlowStepCreateEntry } from "../../../data/data_entry_flow";
import type { DeviceRegistryEntry } from "../../../data/device/device_registry";
import { updateDeviceRegistryEntry } from "../../../data/device/device_registry";
@@ -628,7 +627,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
${
SWITCH_AS_DOMAINS_INVERT.includes(this._switchAsDomain)
? html`
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.invert.label"
@@ -644,7 +643,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
.checked=${!!this.entry.options?.switch_as_x?.invert}
@change=${this._switchAsInvertChanged}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
`
: nothing
} `
@@ -961,7 +960,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
${
this._cameraPrefs
? html`
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.stream.preload_stream"
@@ -978,8 +977,8 @@ export class EntityRegistrySettingsEditor extends LitElement {
.disabled=${this.disabled}
@change=${this._handleCameraPrefsChanged}
></ha-switch>
</ha-row-item>
<ha-row-item>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.stream.stream_orientation"
@@ -1007,97 +1006,103 @@ export class EntityRegistrySettingsEditor extends LitElement {
.value=${this._cameraPrefs.orientation.toString()}
>
</ha-select>
</ha-row-item>
</ha-md-list-item>
`
: nothing
}
<ha-list-base>
${
this.helperConfigEntry &&
this.helperConfigEntry.supports_options &&
this.helperConfigEntry.domain !== "switch_as_x"
? html`
<ha-list-item-button
class="menu-item"
.disabled=${this.disabled}
@click=${this._showOptionsFlow}
${
this.helperConfigEntry &&
this.helperConfigEntry.supports_options &&
this.helperConfigEntry.domain !== "switch_as_x"
? html`
<ha-list-item
class="menu-item"
twoline
hasMeta
.disabled=${this.disabled}
@click=${this._showOptionsFlow}
>
<span
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.configure_state",
{
integration: domainToName(
this.hass.localize,
this.helperConfigEntry.domain
),
}
)}</span
>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.configure_state",
{
integration: domainToName(
this.hass.localize,
this.helperConfigEntry.domain
),
}
)}</span
>
<span slot="supporting-text"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.configure_state_secondary",
{
integration: domainToName(
this.hass.localize,
this.helperConfigEntry.domain
),
}
)}</span
>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
`
: nothing
}
<ha-list-item-button
class="menu-item"
.disabled=${this.disabled}
@click=${this._handleVoiceAssistantsClicked}
<span slot="secondary"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.configure_state_secondary",
{
integration: domainToName(
this.hass.localize,
this.helperConfigEntry.domain
),
}
)}</span
>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`
: nothing
}
<ha-list-item
class="menu-item"
twoline
hasMeta
.disabled=${this.disabled}
@click=${this._handleVoiceAssistantsClicked}
>
<span
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.voice_assistants"
)}</span
>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.voice_assistants"
)}</span
>
<span slot="supporting-text">
${
this.entry.aliases.filter((a) => a !== null).length
? this.entry.aliases
.filter((a): a is string => a !== null)
.join(", ")
: this.hass.localize(
"ui.dialogs.entity_registry.editor.no_aliases"
)
}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
${
domain === "vacuum" &&
stateObj &&
supportsFeature(stateObj, VacuumEntityFeature.CLEAN_AREA)
? html`
<ha-list-item-button
class="menu-item"
.disabled=${this.disabled}
@click=${this._handleVacuumSegmentMappingClicked}
<span slot="secondary">
${
this.entry.aliases.filter((a) => a !== null).length
? this.entry.aliases
.filter((a): a is string => a !== null)
.join(", ")
: this.hass.localize(
"ui.dialogs.entity_registry.editor.no_aliases"
)
}
</span>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
${
domain === "vacuum" &&
stateObj &&
supportsFeature(stateObj, VacuumEntityFeature.CLEAN_AREA)
? html`
<ha-list-item
class="menu-item"
twoline
hasMeta
.disabled=${this.disabled}
@click=${this._handleVacuumSegmentMappingClicked}
>
<span
>${this.hass.localize(
"ui.dialogs.vacuum_segment_mapping.title"
)}</span
>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.vacuum_segment_mapping.title"
)}</span
>
<span slot="supporting-text">
${this.hass.localize(
"ui.dialogs.vacuum_segment_mapping.description"
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
`
: nothing
}
</ha-list-base>
<span slot="secondary">
${this.hass.localize(
"ui.dialogs.vacuum_segment_mapping.description"
)}
</span>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`
: nothing
}
${
this._disabledBy &&
this._disabledBy !== "user" &&
@@ -1115,7 +1120,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
: nothing
}
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.enabled_label"
@@ -1138,7 +1143,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
}
@change=${this._enabledChanged}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
${
this._hiddenBy && this._hiddenBy !== "user"
@@ -1155,7 +1160,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
: nothing
}
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.visible_label"
@@ -1172,12 +1177,12 @@ export class EntityRegistrySettingsEditor extends LitElement {
.disabled=${this.disabled || this._disabledBy}
@change=${this._hiddenChanged}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
${
this.entry.device_id
? html`
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.dialogs.entity_registry.editor.use_device_area"
@@ -1213,7 +1218,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
.disabled=${this.disabled}
@change=${this._useDeviceAreaChanged}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
${
this._areaId || this._noDeviceArea
? html`<ha-area-picker
@@ -1806,10 +1811,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
margin-inline-start: 0;
}
ha-row-item {
--ha-row-item-padding-inline: 0;
}
ha-row-item ha-select {
ha-md-list-item ha-select {
width: auto;
}
ha-input,
@@ -1821,11 +1823,11 @@ export class EntityRegistrySettingsEditor extends LitElement {
width: 100%;
}
.menu-item {
--ha-row-item-padding-inline: 0;
border-radius: var(--ha-border-radius-sm);
margin-top: 3px;
margin-bottom: 3px;
overflow: hidden;
--mdc-list-side-padding: 13px;
}
.entityId {
direction: ltr;
@@ -9,16 +9,15 @@ import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { round } from "../../../common/number/round";
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
import { sanitizeHttpUrl } from "../../../common/url/sanitize-http-url";
import "../../../components/animation/ha-fade-in";
import "../../../components/chart/ha-chart-base";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
import "../../../components/animation/ha-fade-in";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-next";
import "../../../components/ha-md-list-item";
import "../../../components/ha-spinner";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-nav";
import type { ConfigEntry } from "../../../data/config_entries";
import { subscribeConfigEntries } from "../../../data/config_entries";
import type {
@@ -306,29 +305,24 @@ class HaConfigHardwareOverview extends SubscribeMixin(LitElement) {
${
documentationURL
? html`
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.hardware.documentation"
)}
<ha-md-list-item
.href=${documentationURL}
type="link"
target="_blank"
rel="noopener noreferrer"
>
<ha-list-item-button
.href=${documentationURL}
target="_blank"
rel="noopener noreferrer"
<span
>${this.hass.localize(
"ui.panel.config.hardware.documentation"
)}</span
>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.hardware.documentation"
)}</span
>
<span slot="supporting-text"
>${this.hass.localize(
"ui.panel.config.hardware.documentation_description"
)}</span
>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-nav>
<span slot="supporting-text"
>${this.hass.localize(
"ui.panel.config.hardware.documentation_description"
)}</span
>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
`
: nothing
}
@@ -140,13 +140,8 @@ export class DialogHelperDetail extends DirtyStateProviderMixin<
this._domain = params.domain;
this._item = undefined;
if (this._domain && this._domain in HELPERS) {
this._loading = true;
try {
await HELPERS[this._domain].import();
this._initDirtyTracking({ type: "deep" }, undefined);
} finally {
this._loading = false;
}
await HELPERS[this._domain].import();
this._initDirtyTracking({ type: "deep" }, undefined);
}
this._open = true;
await this.updateComplete;
@@ -190,7 +185,7 @@ export class DialogHelperDetail extends DirtyStateProviderMixin<
let content: TemplateResult;
let footer: TemplateResult | typeof nothing = nothing;
if (this._domain && !this._loading) {
if (this._domain) {
content = html`
<div class="form" @value-changed=${this._valueChanged}>
${this._error ? html`<div class="error">${this._error}</div>` : ""}
@@ -1,9 +1,9 @@
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import "../../../components/ha-dialog";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-base";
import { ERROR_STATES, RECOVERABLE_STATES } from "../../../data/config_entries";
import type { HomeAssistant } from "../../../types";
import type { PickConfigEntryDialogParams } from "./show-pick-config-entry-dialog";
@@ -43,22 +43,21 @@ export class DialogPickConfigEntry extends LitElement {
header-title=${title}
@closed=${this._dialogClosed}
>
<ha-list-base>
<ha-md-list>
${this._params.configEntries.map(
(entry) =>
html`<ha-list-item-button
html`<ha-md-list-item
type="button"
@click=${this._itemPicked}
.entry=${entry}
.disabled=${
!ERROR_STATES.includes(entry.state) &&
!RECOVERABLE_STATES.includes(entry.state)
}
><span slot="headline"
>${entry.title}</span
></ha-list-item-button
>${entry.title}</ha-md-list-item
>`
)}
</ha-list-base>
</ha-md-list>
</ha-dialog>
`;
}
@@ -4,17 +4,15 @@ import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import { stopKeydownEnterSpacePropagation } from "../../../../../common/dom/stop_propagation";
import { computeDeviceName } from "../../../../../common/entity/compute_device_name";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import type {
BluetoothAllocationsData,
BluetoothScannerState,
@@ -193,7 +191,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
>
<div class="container">
<ha-card>
<ha-list-base>
<ha-md-list>
${this._renderAdaptersList(enabledDevices)}
${disabledDevices.map(({ device, entry }) => {
const areaName =
@@ -207,7 +205,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
.filter(Boolean)
.join(" · ");
return html`
<ha-list-item-base class="disabled">
<ha-md-list-item class="disabled">
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
<div slot="headline">
${computeDeviceName(device) || entry.title}
@@ -220,12 +218,12 @@ export class BluetoothAdapterInfoPage extends LitElement {
>
${this.hass.localize("ui.common.enable")}
</ha-button>
</ha-list-item-base>
</ha-md-list-item>
`;
})}
${disabledEntriesWithoutDevice.map(
(entry) => html`
<ha-list-item-base class="disabled">
<ha-md-list-item class="disabled">
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
<div slot="headline">${entry.title}</div>
<div slot="supporting-text">
@@ -240,10 +238,10 @@ export class BluetoothAdapterInfoPage extends LitElement {
>
${this.hass.localize("ui.common.enable")}
</ha-button>
</ha-list-item-base>
</ha-md-list-item>
`
)}
</ha-list-base>
</ha-md-list>
<div class="card-actions">
<ha-button
appearance="plain"
@@ -264,13 +262,13 @@ export class BluetoothAdapterInfoPage extends LitElement {
devices: { device: DeviceRegistryEntry; entry: ConfigEntry }[]
) {
if (devices.length === 0) {
return html`<ha-list-item-base>
return html`<ha-md-list-item>
<div slot="headline">
${this.hass.localize(
"ui.panel.config.bluetooth.no_scanner_state_available"
)}
</div>
</ha-list-item-base>`;
</ha-md-list-item>`;
}
return devices.map(({ device, entry }) => {
@@ -316,7 +314,8 @@ export class BluetoothAdapterInfoPage extends LitElement {
);
return html`
<ha-list-item-button
<ha-md-list-item
type="link"
href=${`/config/bluetooth/connection-monitor?source=${btAddress}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -329,7 +328,6 @@ export class BluetoothAdapterInfoPage extends LitElement {
.path=${mdiCogOutline}
.entry=${entry}
@click=${this._openOptionFlow}
@keydown=${stopKeydownEnterSpacePropagation}
.label=${this.hass.localize(
"ui.panel.config.bluetooth.option_flow"
)}
@@ -337,7 +335,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
: nothing
}
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
${
hasMismatch && scannerDetails
? this._renderScannerMismatchWarning(
@@ -501,14 +499,22 @@ export class BluetoothAdapterInfoPage extends LitElement {
margin: 0 auto var(--ha-space-4);
}
ha-list-item-button ha-svg-icon[slot="start"],
ha-list-item-base ha-svg-icon[slot="start"] {
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
ha-md-list-item ha-svg-icon[slot="start"] {
color: var(--secondary-text-color);
}
ha-list-item-base.disabled ha-svg-icon[slot="start"],
ha-list-item-base.disabled [slot="headline"],
ha-list-item-base.disabled [slot="supporting-text"] {
ha-md-list-item.disabled ha-svg-icon[slot="start"],
ha-md-list-item.disabled [slot="headline"],
ha-md-list-item.disabled [slot="supporting-text"] {
opacity: 0.5;
}
@@ -7,16 +7,16 @@ import {
mdiLinkVariant,
mdiVectorPolyline,
} from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-nav";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type {
BluetoothAllocationsData,
BluetoothDeviceData,
@@ -213,12 +213,11 @@ export class BluetoothConfigDashboard extends LitElement {
</ha-button>
</div>
<div class="card-content network-card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.bluetooth.my_network"
)}
>
<ha-list-item-button href="/config/bluetooth/adapter-info">
<ha-md-list>
<ha-md-list-item
type="link"
href="/config/bluetooth/adapter-info"
>
<ha-svg-icon
slot="start"
.path=${mdiAccessPoint}
@@ -230,9 +229,10 @@ export class BluetoothConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
<ha-list-item-button
<ha-md-list-item
type="link"
href="/config/bluetooth/connection-monitor"
>
<ha-svg-icon
@@ -246,9 +246,10 @@ export class BluetoothConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
<ha-list-item-button
<ha-md-list-item
type="link"
href="/config/bluetooth/advertisement-monitor"
>
<ha-svg-icon slot="start" .path=${mdiBroadcast}></ha-svg-icon>
@@ -259,8 +260,8 @@ export class BluetoothConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-nav>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
</div>
@@ -285,6 +286,11 @@ export class BluetoothConfigDashboard extends LitElement {
margin-top: var(--ha-space-6);
}
ha-md-list {
background: none;
padding: 0;
}
.network-card {
overflow: hidden;
}
@@ -9,9 +9,9 @@ import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-nav";
import type { InfraredDevice } from "../../../../../data/infrared";
import "../../../../../layouts/hass-subpage";
import { haStyle } from "../../../../../resources/styles";
@@ -82,12 +82,8 @@ export class InfraredConfigDashboard extends LitElement {
${this.hass.localize("ui.panel.config.infrared.my_devices")}
</div>
<div class="card-content network-card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.infrared.my_devices"
)}
>
<ha-list-item-button href="/config/infrared/devices">
<ha-md-list>
<ha-md-list-item type="link" href="/config/infrared/devices">
<ha-svg-icon slot="start" .path=${mdiRemote}></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
@@ -96,8 +92,8 @@ export class InfraredConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-nav>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
</div>
@@ -122,6 +118,11 @@ export class InfraredConfigDashboard extends LitElement {
margin-top: var(--ha-space-6);
}
ha-md-list {
background: none;
padding: 0;
}
.network-card {
overflow: hidden;
}
@@ -2,26 +2,25 @@ import { mdiDelete, mdiLock, mdiPlus } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { stopKeydownEnterSpacePropagation } from "../../../../../common/dom/stop_propagation";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-dialog";
import "../../../../../components/ha-dialog-footer";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/ha-dialog";
import type {
MatterLockInfo,
MatterLockUser,
} from "../../../../../data/matter-lock";
import {
clearMatterLockUser,
getMatterLockInfo,
getMatterLockUsers,
clearMatterLockUser,
} from "../../../../../data/matter-lock";
import {
showAlertDialog,
@@ -163,10 +162,11 @@ class DialogMatterLockManage extends LitElement {
)}
</p>`
: html`
<ha-list-base>
<ha-md-list>
${occupiedUsers.map(
(user) => html`
<ha-list-item-button
<ha-md-list-item
type="button"
.user=${user}
@click=${this._handleUserClick}
>
@@ -191,12 +191,11 @@ class DialogMatterLockManage extends LitElement {
.path=${mdiDelete}
.user=${user}
@click=${this._handleDeleteUserClick}
@keydown=${stopKeydownEnterSpacePropagation}
></ha-icon-button>
</ha-list-item-button>
</ha-md-list-item>
`
)}
</ha-list-base>
</ha-md-list>
`
}
${
@@ -3,8 +3,8 @@ import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../../common/dom/fire_event";
import "../../../../../../components/ha-icon-next";
import "../../../../../../components/item/ha-list-item-button";
import "../../../../../../components/list/ha-list-base";
import "../../../../../../components/ha-md-list";
import "../../../../../../components/ha-md-list-item";
import type { HomeAssistant } from "../../../../../../types";
import type { MatterAddDeviceStep } from "../dialog-matter-add-device";
import { sharedStyles } from "./matter-add-device-shared-styles";
@@ -23,8 +23,14 @@ class MatterAddDeviceExisting extends LitElement {
</p>
</div>
<ha-list-base>
<ha-list-item-button .step=${"google_home"} @click=${this._onItemClick}>
<ha-md-list>
<ha-md-list-item
interactive
type="button"
.step=${"google_home"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
<img
src="/static/images/logo_google_home.png"
alt=""
@@ -37,8 +43,14 @@ class MatterAddDeviceExisting extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button .step=${"apple_home"} @click=${this._onItemClick}>
</ha-md-list-item>
<ha-md-list-item
interactive
type="button"
.step=${"apple_home"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
<img
src="/static/images/logo_apple_home.png"
alt=""
@@ -51,8 +63,14 @@ class MatterAddDeviceExisting extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button .step=${"generic"} @click=${this._onItemClick}>
</ha-md-list-item>
<ha-md-list-item
interactive
type="button"
.step=${"generic"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
<div class="logo" slot="start">
<ha-svg-icon path=${mdiHomeAutomation}></ha-svg-icon>
</div>
@@ -62,12 +80,15 @@ class MatterAddDeviceExisting extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
`;
}
private _onItemClick(ev) {
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
return;
}
const item = ev.currentTarget as any;
const step = item.step as MatterAddDeviceStep;
fireEvent(this, "step-selected", { step });
@@ -2,8 +2,8 @@ import { LitElement, html } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../../common/dom/fire_event";
import "../../../../../../components/ha-icon-next";
import "../../../../../../components/item/ha-list-item-button";
import "../../../../../../components/list/ha-list-base";
import "../../../../../../components/ha-md-list-item";
import "../../../../../../components/ha-md-list";
import type { HomeAssistant } from "../../../../../../types";
import { sharedStyles } from "./matter-add-device-shared-styles";
@@ -18,8 +18,14 @@ class MatterAddDeviceMain extends LitElement {
${this.hass.localize(`ui.dialogs.matter-add-device.main.question`)}
</p>
</div>
<ha-list-base>
<ha-list-item-button .step=${"new"} @click=${this._onItemClick}>
<ha-md-list>
<ha-md-list-item
interactive
type="button"
.step=${"new"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
<span slot="headline">
${this.hass.localize(
`ui.dialogs.matter-add-device.main.answer_new`
@@ -31,8 +37,14 @@ class MatterAddDeviceMain extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button .step=${"existing"} @click=${this._onItemClick}>
</ha-md-list-item>
<ha-md-list-item
interactive
type="button"
.step=${"existing"}
@click=${this._onItemClick}
@keydown=${this._onItemClick}
>
<span slot="headline">
${this.hass.localize(
`ui.dialogs.matter-add-device.main.answer_existing`
@@ -44,12 +56,15 @@ class MatterAddDeviceMain extends LitElement {
)}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
`;
}
private _onItemClick(ev) {
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
return;
}
const item = ev.currentTarget as any;
const step = item.step as "new" | "existing";
fireEvent(this, "step-selected", { step });
@@ -23,9 +23,10 @@ export const sharedStyles = css`
cursor: pointer;
text-decoration: underline;
}
ha-list-base {
ha-md-list {
padding: 0;
--ha-row-item-padding-inline: var(--horizontal-padding, 16px);
--md-list-item-leading-space: var(--horizontal-padding, 16px);
--md-list-item-trailing-space: var(--horizontal-padding, 16px);
margin-bottom: 16px;
}
ha-input {
@@ -16,9 +16,9 @@ import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-nav";
import type { ConfigEntry } from "../../../../../data/config_entries";
import { getConfigEntries } from "../../../../../data/config_entries";
import { fetchMatterNetworkTopology } from "../../../../../data/matter";
@@ -167,12 +167,9 @@ export class MatterConfigDashboard extends LitElement {
</ha-button>
</div>
<div class="card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.matter.panel.my_network_title"
)}
>
<ha-list-item-button
<ha-md-list>
<ha-md-list-item
type="link"
href=${`/config/devices/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -183,8 +180,9 @@ export class MatterConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button
</ha-md-list-item>
<ha-md-list-item
type="link"
href=${`/config/entities/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiShape}></ha-svg-icon>
@@ -195,8 +193,8 @@ export class MatterConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-nav>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
`;
@@ -206,12 +204,8 @@ export class MatterConfigDashboard extends LitElement {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.matter.panel.options_title"
)}
>
<ha-list-item-button href="/config/matter/options">
<ha-md-list>
<ha-md-list-item type="link" href="/config/matter/options">
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
@@ -224,10 +218,10 @@ export class MatterConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
${
isComponentLoaded(this.hass.config, "thread")
? html`<ha-list-item-button href="/config/thread">
? html`<ha-md-list-item type="link" href="/config/thread">
<ha-svg-icon
slot="start"
.path=${THREAD_ICON}
@@ -243,10 +237,10 @@ export class MatterConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>`
</ha-md-list-item>`
: nothing
}
</ha-list-nav>
</ha-md-list>
</div>
</ha-card>
`;
@@ -313,6 +307,15 @@ export class MatterConfigDashboard extends LitElement {
margin-top: var(--ha-space-6);
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
.network-status div.heading {
display: flex;
align-items: center;
@@ -1,11 +1,12 @@
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/item/ha-row-item";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import {
acceptSharedMatterDevice,
canCommissionMatterExternal,
@@ -54,120 +55,122 @@ class MatterOptionsPage extends LitElement {
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing
}
${
canCommissionMatterExternal(this.hass)
? html`<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._startMobileCommissioning}
>
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning_action"
)}
</ha-button>
</ha-row-item>`
: nothing
}
<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._commission}
>
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device_action"
)}
</ha-button>
</ha-row-item>
<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._acceptSharedDevice}
>
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device_action"
)}
</ha-button>
</ha-row-item>
<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._setWifi}
>
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials_action"
)}
</ha-button>
</ha-row-item>
<ha-row-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._setThread}
>
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials_action"
)}
</ha-button>
</ha-row-item>
<ha-md-list>
${
canCommissionMatterExternal(this.hass)
? html`<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._startMobileCommissioning}
>
${this.hass.localize(
"ui.panel.config.matter.panel.mobile_app_commisioning_action"
)}
</ha-button>
</ha-md-list-item>`
: nothing
}
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._commission}
>
${this.hass.localize(
"ui.panel.config.matter.panel.commission_device_action"
)}
</ha-button>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._acceptSharedDevice}
>
${this.hass.localize(
"ui.panel.config.matter.panel.add_shared_device_action"
)}
</ha-button>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._setWifi}
>
${this.hass.localize(
"ui.panel.config.matter.panel.set_wifi_credentials_action"
)}
</ha-button>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials"
)}
</span>
<span slot="supporting-text">
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials_description"
)}
</span>
<ha-button
appearance="plain"
slot="end"
size="s"
@click=${this._setThread}
>
${this.hass.localize(
"ui.panel.config.matter.panel.set_thread_credentials_action"
)}
</ha-button>
</ha-md-list-item>
</ha-md-list>
</ha-card>
</div>
</hass-subpage>
@@ -320,6 +323,15 @@ class MatterOptionsPage extends LitElement {
max-width: 600px;
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
`,
];
}
@@ -16,11 +16,10 @@ import "../../../../../components/ha-alert";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import type { ConfigEntry } from "../../../../../data/config_entries";
import { getConfigEntries } from "../../../../../data/config_entries";
import { domainToName } from "../../../../../data/integration";
@@ -188,21 +187,22 @@ export class ModbusConfigDashboard extends LitElement {
// removed while the connection was listed has nowhere to link to
if (!holder.entry) {
return html`
<ha-list-item-base class="holder">
<ha-md-list-item class="holder">
<ha-svg-icon
slot="start"
.path=${source === "yaml" ? mdiFileDocumentOutline : mdiPuzzle}
></ha-svg-icon>
<div slot="headline">${holder.id}</div>
${this._renderUnits(holder)}
</ha-list-item-base>
</ha-md-list-item>
`;
}
const { domain, title } = holder.entry;
return html`
<ha-list-item-button
<ha-md-list-item
type="link"
class="holder"
href=${`/config/integrations/integration/${domain}#config_entry=${holder.id}`}
>
@@ -225,19 +225,19 @@ export class ModbusConfigDashboard extends LitElement {
</div>
${this._renderUnits(holder)}
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
`;
}
private _renderSerialPortLink(): TemplateResult {
return html`
<ha-list-item-button class="holder" href="/config/serial">
<ha-md-list-item type="link" class="holder" href="/config/serial">
<ha-svg-icon slot="start" .path=${mdiCableData}></ha-svg-icon>
<div slot="headline">
${this.hass.localize("ui.panel.config.modbus.view_serial_port")}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
`;
}
@@ -256,12 +256,12 @@ export class ModbusConfigDashboard extends LitElement {
private _renderConnectionItem(item: ConnectionListItem): TemplateResult {
return html`
<ha-list-item-base class="connection">
<ha-md-list-item class="connection">
<ha-svg-icon slot="start" .path=${item.icon}></ha-svg-icon>
<div slot="headline">${item.primary}</div>
<div slot="supporting-text">${item.secondary}</div>
${this._renderState(item)}
</ha-list-item-base>
</ha-md-list-item>
${item.holders.map((holder) => this._renderHolder(holder, item.source))}
${
item.serialDevice && isComponentLoaded(this.hass.config, "usb")
@@ -283,9 +283,9 @@ export class ModbusConfigDashboard extends LitElement {
"ui.panel.config.modbus.connections_description"
)}
</div>
<ha-list-base>
<ha-md-list>
${items.map((item) => this._renderConnectionItem(item))}
</ha-list-base>
</ha-md-list>
</div>
</ha-card>
`;
@@ -474,16 +474,23 @@ export class ModbusConfigDashboard extends LitElement {
color: var(--secondary-text-color);
}
ha-list-item-base,
ha-list-item-button {
--ha-row-item-padding-block: var(--ha-space-2);
--ha-row-item-min-height: 0;
ha-md-list {
background: none;
padding: 0;
}
ha-list-item-base.connection:not(:first-child) {
ha-md-list-item {
--md-list-item-top-space: var(--ha-space-2);
--md-list-item-bottom-space: var(--ha-space-2);
--md-list-item-one-line-container-height: 0;
--md-list-item-two-line-container-height: 0;
--md-list-item-three-line-container-height: 0;
}
ha-md-list-item.connection:not(:first-child) {
border-top: 1px solid var(--divider-color);
margin-top: var(--ha-space-2);
--ha-row-item-padding-block: var(--ha-space-4) var(--ha-space-2);
--md-list-item-top-space: var(--ha-space-4);
}
.state {
@@ -505,11 +512,11 @@ export class ModbusConfigDashboard extends LitElement {
background-color: var(--success-color);
}
.holder {
--ha-row-item-padding-inline: var(--ha-space-14) var(--ha-space-4);
ha-md-list-item.holder {
--md-list-item-leading-space: var(--ha-space-14);
}
.holder img[slot="start"] {
ha-md-list-item.holder img[slot="start"] {
width: 24px;
height: 24px;
}
@@ -1,3 +1,7 @@
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import {
mdiAlertCircleOutline,
mdiCheck,
@@ -5,37 +9,32 @@ import {
mdiShape,
mdiTune,
} from "@mdi/js";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { storage } from "../../../../../common/decorators/storage";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-code-editor";
import "../../../../../components/ha-formfield";
import "../../../../../components/ha-icon-next";
import type { HaSelectSelectEvent } from "../../../../../components/ha-select";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import type { HaSelectSelectEvent } from "../../../../../components/ha-select";
import "../../../../../components/ha-switch";
import "../../../../../components/input/ha-input";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/list/ha-list-nav";
import type { ConfigEntry } from "../../../../../data/config_entries";
import { getConfigEntries } from "../../../../../data/config_entries";
import { fetchIntegrationManifest } from "../../../../../data/integration";
import type { Action } from "../../../../../data/script";
import { callExecuteScript } from "../../../../../data/service";
import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow";
import { showOptionsFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-options-flow";
import "../../../../../layouts/hass-subpage";
import { mdiMqttLogo } from "../../../../../resources/mqtt-logo-svg";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import { brandsUrl } from "../../../../../util/brands-url";
import { showToast } from "../../../../../util/toast";
import "./mqtt-subscribe-card";
import { brandsUrl } from "../../../../../util/brands-url";
import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow";
import { fetchIntegrationManifest } from "../../../../../data/integration";
import { mdiMqttLogo } from "../../../../../resources/mqtt-logo-svg";
const qosLevel = ["0", "1", "2"];
@@ -187,12 +186,9 @@ export class MQTTConfigPanel extends LitElement {
${this.hass.localize("ui.panel.config.mqtt.my_network_title")}
</div>
<div class="card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.mqtt.my_network_title"
)}
>
<ha-list-item-button
<ha-md-list>
<ha-md-list-item
type="link"
href=${`/config/devices/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -202,8 +198,9 @@ export class MQTTConfigPanel extends LitElement {
})}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button
</ha-md-list-item>
<ha-md-list-item
type="link"
href=${`/config/entities/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiShape}></ha-svg-icon>
@@ -213,8 +210,8 @@ export class MQTTConfigPanel extends LitElement {
})}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-nav>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
`;
@@ -224,8 +221,8 @@ export class MQTTConfigPanel extends LitElement {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-list-base>
<ha-list-item-button @click=${this._openOptionFlow}>
<ha-md-list>
<ha-md-list-item type="link" @click=${this._openOptionFlow}>
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
<div slot="headline">
${this.hass.localize("ui.panel.config.mqtt.option_flow")}
@@ -236,8 +233,8 @@ export class MQTTConfigPanel extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button @click=${this._openConfigFlow}>
</ha-md-list-item>
<ha-md-list-item type="link" @click=${this._openConfigFlow}>
<ha-svg-icon slot="start" .path=${mdiMqttLogo}></ha-svg-icon>
<div slot="headline">
${this.hass.localize("ui.panel.config.mqtt.config_flow")}
@@ -248,8 +245,8 @@ export class MQTTConfigPanel extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
`;
@@ -407,6 +404,13 @@ export class MQTTConfigPanel extends LitElement {
margin: 0 auto var(--ha-space-4);
max-width: 600px;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
ha-select {
width: 96px;
margin: 0 8px;
@@ -4,9 +4,9 @@ import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-nav";
import { UNAVAILABLE } from "../../../../../data/entity/entity";
import { FALLBACK_DOMAIN_ICONS } from "../../../../../data/icons";
import type { RadioFrequencyTransmitter } from "../../../../../data/radio_frequency";
@@ -73,12 +73,11 @@ export class RadioFrequencyConfigDashboard extends LitElement {
<ha-card class="network-card">
<div class="card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.radio_frequency.title"
)}
>
<ha-list-item-button href="/config/radio-frequency/devices">
<ha-md-list>
<ha-md-list-item
type="link"
href="/config/radio-frequency/devices"
>
<ha-svg-icon
slot="start"
.path=${FALLBACK_DOMAIN_ICONS[DOMAIN]}
@@ -90,8 +89,8 @@ export class RadioFrequencyConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-nav>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
</div>
@@ -112,6 +111,11 @@ export class RadioFrequencyConfigDashboard extends LitElement {
max-width: 600px;
}
ha-md-list {
background: none;
padding: 0;
}
.network-card {
overflow: hidden;
}
@@ -22,11 +22,10 @@ import "../../../../../components/ha-app-icon";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import {
domainToName,
getConfigPanelPath,
@@ -43,13 +42,13 @@ import type {
} from "../../../../../data/usb";
import { listSerialPortsWithUsage } from "../../../../../data/usb";
import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow";
import { mdiEsphomeLogo } from "../../../../../resources/esphome-logo-svg";
import { showSerialPortInfoDialog } from "./show-dialog-serial-port-info";
import "../../../../../layouts/hass-subpage";
import { panelIsReady } from "../../../../../layouts/panel-ready";
import { mdiEsphomeLogo } from "../../../../../resources/esphome-logo-svg";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant, Route } from "../../../../../types";
import { brandsUrl } from "../../../../../util/brands-url";
import { showSerialPortInfoDialog } from "./show-dialog-serial-port-info";
const ESPHOME_HASS_SCHEME = "esphome-hass://";
@@ -271,7 +270,7 @@ export class SerialConfigDashboard extends LitElement {
const href = this._consumerHref(consumer);
return html`
<ha-list-item-button href=${href} class="consumer">
<ha-md-list-item type="link" href=${href} class="consumer">
${
consumer.kind === "config_entry"
? html`<img
@@ -303,13 +302,14 @@ export class SerialConfigDashboard extends LitElement {
}
<div slot="headline">${this._consumerName(consumer)}</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
`;
}
private _renderDiscoveryFlow(flow: SerialPortDiscoveryFlow): TemplateResult {
return html`
<ha-list-item-button
<ha-md-list-item
type="button"
class="consumer"
.flowId=${flow.flow_id}
@click=${this._continueFlow}
@@ -334,13 +334,13 @@ export class SerialConfigDashboard extends LitElement {
})}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
`;
}
private _renderModbusLink(): TemplateResult {
return html`
<ha-list-item-button class="consumer" href="/config/modbus">
<ha-md-list-item type="link" class="consumer" href="/config/modbus">
<ha-svg-icon
slot="start"
.path=${mdiTransitConnectionVariant}
@@ -349,7 +349,7 @@ export class SerialConfigDashboard extends LitElement {
${this.hass.localize("ui.panel.config.serial.used_by_modbus")}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
`;
}
@@ -378,7 +378,7 @@ export class SerialConfigDashboard extends LitElement {
: undefined;
return html`
<ha-list-item-base class="port">
<ha-md-list-item class="port">
<ha-svg-icon
slot="start"
class=${item.port.present ? "" : "disconnected"}
@@ -415,7 +415,7 @@ export class SerialConfigDashboard extends LitElement {
></ha-icon-button>`
: nothing
}
</ha-list-item-base>
</ha-md-list-item>
${item.consumers.map((consumer) => this._renderConsumer(consumer))}
${this._usedByModbus(item.port) ? this._renderModbusLink() : nothing}
${item.discoveryFlows.map((flow) => this._renderDiscoveryFlow(flow))}
@@ -438,9 +438,9 @@ export class SerialConfigDashboard extends LitElement {
<div class="card-header">${header}</div>
<div class="card-content">
<div class="description">${description}</div>
<ha-list-base>
<ha-md-list>
${items.map((item) => this._renderPortItem(item))}
</ha-list-base>
</ha-md-list>
</div>
</ha-card>
`;
@@ -688,28 +688,35 @@ export class SerialConfigDashboard extends LitElement {
color: var(--secondary-text-color);
}
ha-list-item-base,
ha-list-item-button {
--ha-row-item-padding-block: var(--ha-space-2);
--ha-row-item-min-height: 0;
ha-md-list {
background: none;
padding: 0;
}
ha-list-item-base.port:not(:first-child) {
ha-md-list-item {
--md-list-item-top-space: var(--ha-space-2);
--md-list-item-bottom-space: var(--ha-space-2);
--md-list-item-one-line-container-height: 0;
--md-list-item-two-line-container-height: 0;
--md-list-item-three-line-container-height: 0;
}
ha-md-list-item.port:not(:first-child) {
border-top: 1px solid var(--divider-color);
margin-top: var(--ha-space-2);
--ha-row-item-padding-block: var(--ha-space-4) var(--ha-space-2);
--md-list-item-top-space: var(--ha-space-4);
}
ha-list-item-base .disconnected {
ha-md-list-item .disconnected {
color: var(--disabled-text-color);
}
.consumer {
--ha-row-item-padding-inline: var(--ha-space-14) var(--ha-space-4);
ha-md-list-item.consumer {
--md-list-item-leading-space: var(--ha-space-14);
}
.consumer img[slot="start"],
.consumer ha-app-icon[slot="start"] {
ha-md-list-item.consumer img[slot="start"],
ha-md-list-item.consumer ha-app-icon[slot="start"] {
width: 24px;
height: 24px;
}
@@ -16,20 +16,18 @@ import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../../../../common/config/is_component_loaded";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import { navigate } from "../../../../../common/navigate";
import "../../../../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../../../../components/buttons/ha-progress-button";
import { animationStyles } from "../../../../../resources/theme/animations.globals";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import { animationStyles } from "../../../../../resources/theme/animations.globals";
import "../../../../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../../../../components/buttons/ha-progress-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/list/ha-list-nav";
import type { ConfigEntry } from "../../../../../data/config_entries";
import { getConfigEntries } from "../../../../../data/config_entries";
import type {
@@ -221,12 +219,9 @@ class ZHAConfigDashboard extends LitElement {
</ha-button>
</div>
<div class="card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.zha.configuration_page.my_network_title"
)}
>
<ha-list-item-button
<ha-md-list>
<ha-md-list-item
type="link"
href=${`/config/devices/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -237,8 +232,9 @@ class ZHAConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button
</ha-md-list-item>
<ha-md-list-item
type="link"
href=${`/config/entities/dashboard?historyBack=1&config_entry=${this._configEntry?.entry_id}`}
>
<ha-svg-icon slot="start" .path=${mdiShape}></ha-svg-icon>
@@ -249,8 +245,8 @@ class ZHAConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button href="/config/zha/groups">
</ha-md-list-item>
<ha-md-list-item type="link" href="/config/zha/groups">
<ha-svg-icon
slot="start"
.path=${mdiFolderMultipleOutline}
@@ -275,8 +271,8 @@ class ZHAConfigDashboard extends LitElement {
}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-nav>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
`;
@@ -292,12 +288,8 @@ class ZHAConfigDashboard extends LitElement {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.zha.configuration_page.options_title"
)}
>
<ha-list-item-button href="/config/zha/options">
<ha-md-list>
<ha-md-list-item type="link" href="/config/zha/options">
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
@@ -310,8 +302,8 @@ class ZHAConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button href="/config/zha/network-info">
</ha-md-list-item>
<ha-md-list-item type="link" href="/config/zha/network-info">
<ha-svg-icon
slot="start"
.path=${mdiInformationOutline}
@@ -327,10 +319,13 @@ class ZHAConfigDashboard extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
${dynamicSections.map(
(section) => html`
<ha-list-item-button href=${`/config/zha/section/${section}`}>
<ha-md-list-item
type="link"
href=${`/config/zha/section/${section}`}
>
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
<div slot="headline">
${
@@ -340,10 +335,10 @@ class ZHAConfigDashboard extends LitElement {
}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
`
)}
</ha-list-nav>
</ha-md-list>
</div>
</ha-card>
`;
@@ -353,8 +348,8 @@ class ZHAConfigDashboard extends LitElement {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-list-base>
<ha-list-item-base>
<ha-md-list>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zha.configuration_page.download_backup"
@@ -377,8 +372,8 @@ class ZHAConfigDashboard extends LitElement {
"ui.panel.config.zha.configuration_page.download_backup_action"
)}
</ha-progress-button>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zha.configuration_page.migrate_radio"
@@ -399,8 +394,8 @@ class ZHAConfigDashboard extends LitElement {
"ui.panel.config.zha.configuration_page.migrate_radio_action"
)}
</ha-button>
</ha-list-item-base>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
`;
@@ -537,6 +532,15 @@ class ZHAConfigDashboard extends LitElement {
padding: var(--ha-space-12);
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
.network-status div.heading {
display: flex;
align-items: center;
@@ -4,8 +4,8 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import type { ZHANetworkSettings } from "../../../../../data/zha";
import { fetchZHANetworkSettings } from "../../../../../data/zha";
import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
@@ -53,8 +53,8 @@ class ZHANetworkInfoPage extends LitElement {
<ha-card>
${
this._networkSettings
? html`<ha-list-base>
<ha-list-item-base>
? html`<ha-md-list>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.channel_label"
@@ -73,16 +73,16 @@ class ZHANetworkInfoPage extends LitElement {
.path=${mdiPencil}
@click=${this._showChannelMigrationDialog}
></ha-icon-button>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">PAN ID</span>
<span slot="supporting-text"
>${
this._networkSettings.settings.network_info.pan_id
}</span
>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">Extended PAN ID</span>
<span slot="supporting-text"
>${
@@ -90,14 +90,14 @@ class ZHANetworkInfoPage extends LitElement {
.extended_pan_id
}</span
>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">Coordinator IEEE</span>
<span slot="supporting-text"
>${this._networkSettings.settings.node_info.ieee}</span
>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.radio_type"
@@ -106,8 +106,8 @@ class ZHANetworkInfoPage extends LitElement {
<span slot="supporting-text"
>${this._networkSettings.radio_type}</span
>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.serial_port"
@@ -116,12 +116,12 @@ class ZHANetworkInfoPage extends LitElement {
<span slot="supporting-text"
>${this._networkSettings.device.path}</span
>
</ha-list-item-base>
</ha-md-list-item>
${
this._networkSettings.device.baudrate &&
!this._networkSettings.device.path.startsWith("socket://")
? html`
<ha-list-item-base>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.baudrate"
@@ -130,11 +130,11 @@ class ZHANetworkInfoPage extends LitElement {
<span slot="supporting-text"
>${this._networkSettings.device.baudrate}</span
>
</ha-list-item-base>
</ha-md-list-item>
`
: nothing
}
</ha-list-base>`
</ha-md-list>`
: nothing
}
</ha-card>
@@ -174,6 +174,19 @@ class ZHANetworkInfoPage extends LitElement {
max-width: 600px;
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
--md-list-item-supporting-text-size: var(
--md-list-item-label-text-size,
var(--md-sys-typescale-body-large-size, 1rem)
);
}
`,
];
}
@@ -4,13 +4,13 @@ import { customElement, property, state } from "lit/decorators";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/buttons/ha-progress-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-select";
import "../../../../../components/ha-switch";
import type { HaSwitch } from "../../../../../components/ha-switch";
import "../../../../../components/input/ha-input";
import type { HaInput } from "../../../../../components/input/ha-input";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/ha-switch";
import type { HaSwitch } from "../../../../../components/ha-switch";
import type { ZHAConfiguration } from "../../../../../data/zha";
import {
fetchZHAConfiguration,
@@ -115,8 +115,8 @@ class ZHAOptionsPage extends LitElement {
${
this._configuration
? html`
<ha-list-base>
<ha-list-item-base>
<ha-md-list>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.enable_identify_on_join_label"
@@ -135,8 +135,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._enableIdentifyOnJoinChanged}
></ha-switch>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.default_light_transition_label"
@@ -160,8 +160,8 @@ class ZHAOptionsPage extends LitElement {
>
<span slot="end">s</span>
</ha-input>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.enhanced_light_transition_label"
@@ -180,8 +180,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._enhancedLightTransitionChanged}
></ha-switch>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.light_transitioning_flag_label"
@@ -200,8 +200,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._lightTransitioningFlagChanged}
></ha-switch>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.group_members_assume_state_label"
@@ -220,8 +220,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._groupMembersAssumeStateChanged}
></ha-switch>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.consider_unavailable_mains_label"
@@ -242,11 +242,11 @@ class ZHAOptionsPage extends LitElement {
.options=${this._getUnavailableTimeoutOptions(7200)}
@selected=${this._mainsUnavailableChanged}
></ha-select>
</ha-list-item-base>
</ha-md-list-item>
${
this._customMains
? html`
<ha-list-item-base>
<ha-md-list-item>
<ha-input
slot="end"
type="number"
@@ -261,11 +261,11 @@ class ZHAOptionsPage extends LitElement {
>
<span slot="end">s</span>
</ha-input>
</ha-list-item-base>
</ha-md-list-item>
`
: nothing
}
<ha-list-item-base>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.consider_unavailable_battery_label"
@@ -286,11 +286,11 @@ class ZHAOptionsPage extends LitElement {
.options=${this._getUnavailableTimeoutOptions(21600)}
@selected=${this._batteryUnavailableChanged}
></ha-select>
</ha-list-item-base>
</ha-md-list-item>
${
this._customBattery
? html`
<ha-list-item-base>
<ha-md-list-item>
<ha-input
slot="end"
type="number"
@@ -305,11 +305,11 @@ class ZHAOptionsPage extends LitElement {
>
<span slot="end">s</span>
</ha-input>
</ha-list-item-base>
</ha-md-list-item>
`
: nothing
}
<ha-list-item-base>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.zha.configuration_page.enable_mains_startup_polling_label"
@@ -328,8 +328,8 @@ class ZHAOptionsPage extends LitElement {
}
@change=${this._enableMainsStartupPollingChanged}
></ha-switch>
</ha-list-item-base>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
<div class="card-actions">
<ha-progress-button
appearance="filled"
@@ -463,6 +463,15 @@ class ZHAOptionsPage extends LitElement {
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
ha-select,
ha-input {
min-width: 210px;
@@ -1,12 +1,12 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { css, html, LitElement, nothing } from "lit";
import { fireEvent } from "../../../../../../common/dom/fire_event";
import type { HomeAssistant } from "../../../../../../types";
import "../../../../../../components/ha-md-list";
import "../../../../../../components/ha-md-list-item";
import "../../../../../../components/ha-alert";
import "../../../../../../components/ha-icon-next";
import "../../../../../../components/item/ha-list-item-button";
import "../../../../../../components/list/ha-list-base";
@customElement("zwave-js-add-node-select-method")
export class ZWaveJsAddNodeSelectMethod extends LitElement {
@@ -26,10 +26,12 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
>`
: nothing
}
<ha-list-base>
<ha-md-list>
${
!this.hideQrWebcam
? html`<ha-list-item-button
? html`<ha-md-list-item
interactive
type="button"
@click=${this._selectMethod}
.value=${"qr_code_webcam"}
.disabled=${!window.isSecureContext}
@@ -45,10 +47,12 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>`
</ha-md-list-item>`
: nothing
}
<ha-list-item-button
<ha-md-list-item
interactive
type="button"
@click=${this._selectMethod}
.value=${"qr_code_manual"}
>
@@ -63,8 +67,10 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button
</ha-md-list-item>
<ha-md-list-item
interactive
type="button"
@click=${this._selectMethod}
.value=${"search_device"}
>
@@ -79,8 +85,8 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-base>
</ha-md-list-item>
</ha-md-list>
`;
}
@@ -90,6 +96,12 @@ export class ZWaveJsAddNodeSelectMethod extends LitElement {
fireEvent(this, "z-wave-method-selected", { method });
}
}
static styles = css`
ha-md-list {
padding: 0;
}
`;
}
declare global {
@@ -2,35 +2,32 @@ import { mdiAccountKey, mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
import { fireEvent } from "../../../../../common/dom/fire_event";
import {
stopKeydownEnterSpacePropagation,
stopPropagation,
} from "../../../../../common/dom/stop_propagation";
import type { LocalizeKeys } from "../../../../../common/translations/localize";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-dialog";
import "../../../../../components/ha-dialog-footer";
import "../../../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../../../components/ha-dropdown";
import "../../../../../components/ha-dropdown-item";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/ha-dialog";
import type {
ZwaveCredentialCapabilities,
ZwaveUser,
} from "../../../../../data/zwave_js-credentials";
import {
canAddZwaveUser,
compatibleUserTypes,
deleteZwaveAllUsers,
deleteZwaveUser,
getZwaveCredentialCapabilities,
getZwaveUsers,
compatibleUserTypes,
} from "../../../../../data/zwave_js-credentials";
import {
showAlertDialog,
@@ -223,10 +220,11 @@ class DialogZwaveCredentialManage extends LitElement {
)}
</p>`
: html`
<ha-list-base>
<ha-md-list>
${activeUsers.map(
(user) => html`
<ha-list-item-button
<ha-md-list-item
type="button"
data-user-id=${user.user_id}
@click=${this._handleUserClick}
>
@@ -266,12 +264,11 @@ class DialogZwaveCredentialManage extends LitElement {
data-user-id=${user.user_id}
?disabled=${this._busy}
@click=${this._handleDeleteUserClick}
@keydown=${stopKeydownEnterSpacePropagation}
></ha-icon-button>
</ha-list-item-button>
</ha-md-list-item>
`
)}
</ha-list-base>
</ha-md-list>
`
}
</div>
@@ -8,14 +8,14 @@ import { computeAreaName } from "../../../../../../common/entity/compute_area_na
import { computeDeviceNameDisplay } from "../../../../../../common/entity/compute_device_name";
import { getDeviceArea } from "../../../../../../common/entity/context/get_device_context";
import { caseInsensitiveStringCompare } from "../../../../../../common/string/compare";
import "../../../../../../components/animation/ha-fade-in";
import "../../../../../../components/ha-button";
import "../../../../../../components/ha-dialog";
import "../../../../../../components/ha-dialog-footer";
import "../../../../../../components/ha-domain-icon";
import "../../../../../../components/animation/ha-fade-in";
import "../../../../../../components/ha-md-list";
import "../../../../../../components/ha-md-list-item";
import "../../../../../../components/ha-spinner";
import "../../../../../../components/item/ha-list-item-base";
import "../../../../../../components/list/ha-list-base";
import {
configEntriesContext,
devicesContext,
@@ -115,13 +115,13 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
)}
</p>`
: this._zwaveDevices
? html`<ha-list-base>
? html`<ha-md-list>
${this._filteredDevices(
this._progress,
this._zwaveDevices
).map(
(device) => html`
<ha-list-item-base>
<ha-md-list-item>
<ha-domain-icon
slot="start"
.domain=${device.domain}
@@ -129,10 +129,10 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
></ha-domain-icon>
<span slot="headline">${device.name}</span>
<span slot="supporting-text">${device.areaName}</span>
</ha-list-item-base>
</ha-md-list-item>
`
)}
</ha-list-base>`
</ha-md-list>`
: nothing
}
</ha-dialog>
@@ -208,14 +208,16 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
static get styles(): CSSResultGroup {
return [
css`
ha-list-base {
--ha-list-gap: var(--ha-space-2);
ha-md-list {
gap: var(--ha-space-2);
min-height: 300px;
}
ha-list-item-base {
--ha-row-item-min-height: 0;
--ha-row-item-padding-block: var(--ha-space-1);
--ha-row-item-padding-inline: var(--ha-space-2);
ha-md-list-item {
--md-list-item-two-line-container-height: 0;
--md-list-item-top-space: var(--ha-space-1);
--md-list-item-bottom-space: var(--ha-space-1);
--md-list-item-leading-space: var(--ha-space-2);
--md-list-item-trailing-space: var(--ha-space-2);
border: var(--ha-border-width-sm) solid
var(--ha-color-border-neutral-normal);
border-radius: var(--ha-border-radius-lg);
@@ -23,12 +23,10 @@ import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-button";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/list/ha-list-nav";
import "../../../../../components/progress/ha-progress-ring";
import type { ConfigEntry } from "../../../../../data/config_entries";
import {
@@ -307,12 +305,9 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
</ha-button>
</div>
<div class="card-content network-card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.network_card_title"
)}
>
<ha-list-item-button
<ha-md-list>
<ha-md-list-item
type="link"
href=${`/config/devices/dashboard?historyBack=1&config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiDevices}></ha-svg-icon>
@@ -323,8 +318,9 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button
</ha-md-list-item>
<ha-md-list-item
type="link"
href=${`/config/entities/dashboard?historyBack=1&config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiShape}></ha-svg-icon>
@@ -335,10 +331,11 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
${
this._provisioningEntries?.length
? html`<ha-list-item-button
? html`<ha-md-list-item
type="link"
href=${`provisioned?config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiQrcode}></ha-svg-icon>
@@ -349,10 +346,10 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>`
</ha-md-list-item>`
: nothing
}
</ha-list-nav>
</ha-md-list>
</div>
</ha-card>
`;
@@ -362,12 +359,9 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
return html`
<ha-card class="nav-card">
<div class="card-content">
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.zwave_js.navigation.general"
)}
>
<ha-list-item-button
<ha-md-list>
<ha-md-list-item
type="link"
href=${`options?config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiTune}></ha-svg-icon>
@@ -382,8 +376,9 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button
</ha-md-list-item>
<ha-md-list-item
type="link"
href=${`statistics?config_entry=${this.configEntryId}`}
>
<ha-svg-icon slot="start" .path=${mdiPoll}></ha-svg-icon>
@@ -398,8 +393,9 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button
</ha-md-list-item>
<ha-md-list-item
type="link"
href=${`logs?config_entry=${this.configEntryId}`}
>
<ha-svg-icon
@@ -417,8 +413,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button href="/config/analytics?section=zwave">
</ha-md-list-item>
<ha-md-list-item type="link" href="/config/analytics?section=zwave">
<ha-svg-icon slot="start" .path=${mdiChartBox}></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
@@ -440,8 +436,9 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
}
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
<ha-list-item-button
</ha-md-list-item>
<ha-md-list-item
type="link"
href=${`network-info?config_entry=${this.configEntryId}`}
>
<ha-svg-icon
@@ -459,8 +456,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-list-nav>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-card>
`;
@@ -493,8 +490,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
)}
${this._restoreProgress}%
</div>`
: html`<ha-list-base>
<ha-list-item-base>
: html`<ha-md-list>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.nvm_backup.download_backup"
@@ -519,8 +516,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
"ui.panel.config.zwave_js.dashboard.nvm_backup.download_action"
)}
</ha-button>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.nvm_backup.restore_backup"
@@ -548,8 +545,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
@change=${this._handleRestoreFileSelected}
style="display: none"
/>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.nvm_backup.migrate"
@@ -570,8 +567,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
"ui.panel.config.zwave_js.dashboard.nvm_backup.migrate_action"
)}
</ha-button>
</ha-list-item-base>
</ha-list-base>`
</ha-md-list-item>
</ha-md-list>`
}
</div>
</ha-card>
@@ -908,6 +905,15 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
max-width: 600px;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
.network-card .card-header {
display: flex;
align-items: center;
@@ -3,8 +3,8 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import type { ZWaveJSControllerStatisticsUpdatedMessage } from "../../../../../data/zwave_js";
import { subscribeZwaveControllerStatistics } from "../../../../../data/zwave_js";
import "../../../../../layouts/hass-subpage";
@@ -60,7 +60,7 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
>
<div class="container">
<ha-card>
<ha-list-base>
<ha-md-list>
${this._renderStat("messages_tx")}
${this._renderStat("messages_rx")}
${this._renderStat("messages_dropped_tx")}
@@ -69,7 +69,7 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
${this._renderStat("timeout_ack")}
${this._renderStat("timeout_response")}
${this._renderStat("timeout_callback")}
</ha-list-base>
</ha-md-list>
</ha-card>
</div>
</hass-subpage>
@@ -78,7 +78,7 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
private _renderStat(key: string) {
return html`
<ha-list-item-base>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
`ui.panel.config.zwave_js.dashboard.statistics.${key}.label`
@@ -90,7 +90,7 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
)}
</span>
<span slot="end">${this._statistics?.[key] ?? 0}</span>
</ha-list-item-base>
</ha-md-list-item>
`;
}
@@ -104,6 +104,15 @@ class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
max-width: 600px;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
span[slot="end"] {
font-size: 0.95em;
color: var(--primary-text-color);
@@ -2,8 +2,8 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import type { ZWaveJSNetwork } from "../../../../../data/zwave_js";
import { fetchZwaveNetworkStatus } from "../../../../../data/zwave_js";
import "../../../../../layouts/hass-subpage";
@@ -49,8 +49,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<ha-card>
${
this._network
? html`<ha-list-base>
<ha-list-item-base>
? html`<ha-md-list>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.home_id"
@@ -59,8 +59,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<span slot="supporting-text">
${formatHomeIdAsHex(this._network.controller.home_id)}
</span>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.driver_version"
@@ -69,8 +69,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<span slot="supporting-text">
${this._network.client.driver_version}
</span>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.server_version"
@@ -79,8 +79,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<span slot="supporting-text">
${this._network.client.server_version}
</span>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.dashboard.server_url"
@@ -89,8 +89,8 @@ class ZWaveJSNetworkInfoPage extends LitElement {
<span slot="supporting-text">
${this._network.client.ws_server_url}
</span>
</ha-list-item-base>
</ha-list-base>`
</ha-md-list-item>
</ha-md-list>`
: nothing
}
</ha-card>
@@ -111,6 +111,19 @@ class ZWaveJSNetworkInfoPage extends LitElement {
max-width: 600px;
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
--md-list-item-supporting-text-size: var(
--md-list-item-label-text-size,
var(--md-sys-typescale-body-large-size, 1rem)
);
}
`,
];
}
@@ -3,8 +3,8 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/list/ha-list-base";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import type {
ZWaveJSClient,
ZWaveJSNetwork,
@@ -61,8 +61,8 @@ class ZWaveJSOptionsPage extends LitElement {
<ha-card>
${
this._network
? html`<ha-list-base>
<ha-list-item-base>
? html`<ha-md-list>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.common.rebuild_network_routes"
@@ -84,8 +84,8 @@ class ZWaveJSOptionsPage extends LitElement {
"ui.panel.config.zwave_js.dashboard.rebuild_routes_action"
)}
</ha-button>
</ha-list-item-base>
<ha-list-item-base>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
${this.hass.localize(
"ui.panel.config.zwave_js.common.remove_node"
@@ -113,8 +113,8 @@ class ZWaveJSOptionsPage extends LitElement {
"ui.panel.config.zwave_js.dashboard.remove_node_action"
)}
</ha-button>
</ha-list-item-base>
</ha-list-base>`
</ha-md-list-item>
</ha-md-list>`
: nothing
}
</ha-card>
@@ -149,6 +149,15 @@ class ZWaveJSOptionsPage extends LitElement {
max-width: 600px;
margin: auto;
}
ha-md-list {
background: none;
padding: 0;
}
ha-md-list-item {
--md-item-overflow: visible;
}
`,
];
}
@@ -4,11 +4,12 @@ import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { relativeTime } from "../../../common/datetime/relative_time";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button";
import "../../../components/ha-dialog";
import "../../../components/ha-dialog-footer";
import "../../../components/ha-switch";
import "../../../components/ha-dialog";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/item/ha-row-item";
import "../../../components/ha-switch";
import type { BackupConfig } from "../../../data/backup";
import { fetchBackupConfig } from "../../../data/backup";
import { getSupervisorUpdateConfig } from "../../../data/supervisor/update";
@@ -157,23 +158,25 @@ export class DialogLabsPreviewFeatureEnable
${
createBackupTexts
? html`
<ha-row-item>
<span slot="headline">${createBackupTexts.title}</span>
${
createBackupTexts.description
? html`
<span slot="supporting-text">
${createBackupTexts.description}
</span>
`
: nothing
}
<ha-switch
slot="end"
.checked=${this._createBackup}
@change=${this._createBackupChanged}
></ha-switch>
</ha-row-item>
<ha-md-list>
<ha-md-list-item>
<span slot="headline">${createBackupTexts.title}</span>
${
createBackupTexts.description
? html`
<span slot="supporting-text">
${createBackupTexts.description}
</span>
`
: nothing
}
<ha-switch
slot="end"
.checked=${this._createBackup}
@change=${this._createBackupChanged}
></ha-switch>
</ha-md-list-item>
</ha-md-list>
`
: nothing
}
@@ -208,8 +211,10 @@ export class DialogLabsPreviewFeatureEnable
color: var(--secondary-text-color);
}
ha-row-item {
--ha-row-item-padding-inline: var(--ha-space-6);
ha-md-list {
background: none;
--md-list-item-leading-space: var(--ha-space-6);
--md-list-item-trailing-space: var(--ha-space-6);
margin: 0;
padding: 0;
border-top: var(--ha-border-width-sm) solid var(--divider-color);
@@ -2,11 +2,11 @@ import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import "../../../components/ha-card";
import "../../../components/ha-icon-next";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-nav";
import "../../../layouts/hass-subpage";
import "../../../components/ha-card";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import "../../../components/ha-icon-next";
import type { HomeAssistant, Route } from "../../../types";
import "./ha-config-http-form";
import "./ha-config-network";
@@ -57,14 +57,10 @@ class HaConfigSectionNetwork extends LitElement {
"ui.panel.config.network.discovery.title"
)}
>
<ha-list-nav
.ariaLabel=${this.hass.localize(
"ui.panel.config.network.discovery.title"
)}
>
<ha-md-list>
${NETWORK_BROWSERS.map(
(domain) => html`
<ha-list-item-button href="/config/${domain}">
<ha-md-list-item type="link" href="/config/${domain}">
<div slot="headline">
${this.hass.localize(
`ui.panel.config.network.discovery.${domain}`
@@ -76,10 +72,10 @@ class HaConfigSectionNetwork extends LitElement {
)}
</div>
<ha-icon-next slot="end"></ha-icon-next>
</ha-list-item-button>
</ha-md-list-item>
`
)}
</ha-list-nav>
</ha-md-list>
</ha-card>
`
: ""
@@ -106,6 +102,10 @@ class HaConfigSectionNetwork extends LitElement {
margin-bottom: 24px;
max-width: 600px;
}
.discovery-card ha-md-list {
background: none;
padding-top: 0;
}
`;
}
@@ -7,12 +7,12 @@ import { isIPAddress } from "../../../common/string/is_ip_address";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
import "../../../components/ha-md-list-item";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import type { HaInput } from "../../../components/input/ha-input";
import "../../../components/input/ha-input-copy";
import type { HaInputCopy } from "../../../components/input/ha-input-copy";
import "../../../components/item/ha-row-item";
import type { CloudStatus } from "../../../data/cloud";
import { fetchCloudStatus } from "../../../data/cloud";
import { saveCoreConfig } from "../../../data/core";
@@ -161,7 +161,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
${
hasCloud
? html`
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.url.external_use_ha_cloud"
@@ -173,7 +173,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
.checked=${this._cloudChecked}
@change=${this._toggleCloud}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
`
: nothing
}
@@ -261,7 +261,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
<h4>
${this.hass.localize("ui.panel.config.url.internal_url_label")}
</h4>
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.url.internal_url_automatic"
@@ -278,7 +278,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
.checked=${!this._showCustomInternalUrl}
@change=${this._toggleInternalAutomatic}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
<div class="url-container">
<ha-input-copy
@@ -466,10 +466,12 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
flex: 1;
}
ha-row-item {
--ha-row-item-padding-block: 0;
--ha-row-item-padding-inline: 0;
--ha-row-item-min-height: 48px;
ha-md-list-item {
--md-list-item-top-space: 0;
--md-list-item-bottom-space: 0;
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-list-item-two-line-container-height: 48px;
}
.no-wrap {
@@ -3,8 +3,8 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-card";
import "../../../components/ha-list";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-base";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import type {
IntegrationManifest,
IntegrationSetup,
@@ -40,7 +40,7 @@ class IntegrationsStartupTime extends LitElement {
}
return html`
<ha-list-base>
<ha-md-list>
${this._setups?.map((setup) => {
const manifest = this._manifests && this._manifests[setup.domain];
const docLink =
@@ -55,7 +55,7 @@ class IntegrationsStartupTime extends LitElement {
const setupSeconds = setup.seconds?.toFixed(2);
return html`
<ha-list-item-button .href=${docLink} target="_blank">
<ha-md-list-item .href=${docLink} type="link" target="_blank">
<img
alt=""
loading="lazy"
@@ -71,17 +71,17 @@ class IntegrationsStartupTime extends LitElement {
referrerpolicy="no-referrer"
slot="start"
/>
<span slot="headline">
<span>
${domainToName(this.hass.localize, setup.domain, manifest)}
</span>
<span slot="supporting-text">${setup.domain}</span>
<div slot="end">
${setupSeconds ? html`${setupSeconds} s` : ""}
</div>
</ha-list-item-button>
</ha-md-list-item>
`;
})}
</ha-list-base>
</ha-md-list>
`;
}
@@ -21,15 +21,14 @@ import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/ha-md-list-item";
import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/ha-svg-icon";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/item/ha-list-item-button";
import "../../../components/item/ha-row-item";
import "../../../components/list/ha-list-base";
import "../../../components/voice-assistant-brand-icon";
import type { AssistPipeline } from "../../../data/assist_pipeline";
import {
@@ -53,6 +52,7 @@ import { showVoiceCommandDialog } from "../../../dialogs/voice-command-dialog/sh
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showVoiceAssistantPipelineDetailDialog } from "./show-dialog-voice-assistant-pipeline-detail";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
@customElement("assist-pref")
export class AssistPref extends LitElement {
@@ -123,14 +123,17 @@ export class AssistPref extends LitElement {
class="icon-link"
></ha-icon-button>
</div>
<ha-list-base>
<ha-list>
${this._pipelines.map(
(pipeline) => html`
<ha-list-item-button
<ha-list-item
twoline
hasMeta
role="button"
.id=${pipeline.id}
@click=${this._editPipeline}
>
<span slot="headline">
<span>
${pipeline.name}
${
this._preferred === pipeline.id
@@ -138,14 +141,13 @@ export class AssistPref extends LitElement {
: ""
}
</span>
<span slot="supporting-text">
<span slot="secondary">
${formatLanguageCode(pipeline.language, this.hass.locale)}
</span>
<ha-dropdown
slot="end"
slot="meta"
placement="bottom-end"
@click=${stopPropagation}
@keydown=${this._handleDropdownKeydown}
@wa-select=${this._handlePipelineMenuAction}
>
<ha-icon-button
@@ -197,10 +199,10 @@ export class AssistPref extends LitElement {
<ha-svg-icon slot="icon" .path=${mdiTrashCan}></ha-svg-icon>
</ha-dropdown-item>
</ha-dropdown>
</ha-list-item-button>
</ha-list-item>
`
)}
</ha-list-base>
</ha-list>
<ha-button
appearance="filled"
@click=${this._addPipeline}
@@ -212,7 +214,7 @@ export class AssistPref extends LitElement {
)}
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
</ha-button>
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.voice_assistants.expose.expose_new_entities"
@@ -229,7 +231,7 @@ export class AssistPref extends LitElement {
.disabled=${this._exposeNew === undefined}
@change=${this._exposeNewToggleChanged}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
<div class="card-actions">
<ha-button
appearance="plain"
@@ -405,12 +407,6 @@ export class AssistPref extends LitElement {
});
}
private _handleDropdownKeydown(ev: KeyboardEvent) {
if (ev.key === " " || ev.key === "Enter") {
ev.stopPropagation();
}
}
static styles = css`
a {
color: var(--primary-color);
@@ -432,7 +428,14 @@ export class AssistPref extends LitElement {
direction: var(--direction);
color: var(--secondary-text-color);
}
ha-list-item-button span ha-svg-icon {
ha-list-item {
--mdc-list-item-meta-size: auto;
--mdc-list-item-meta-display: flex;
--mdc-list-side-padding-right: 8px;
--mdc-list-side-padding-left: 16px;
}
ha-list-item span ha-svg-icon {
color: currentColor;
width: 16px;
}
@@ -5,12 +5,11 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
import "../../../components/ha-button";
import "../../../components/ha-md-list-item";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/item/ha-row-item";
import "../../../components/voice-assistant-brand-icon";
import type { CloudStatusLoggedIn } from "../../../data/cloud";
import { updateCloudPref } from "../../../data/cloud";
import type { ExposeEntitySettings } from "../../../data/expose";
@@ -19,6 +18,7 @@ import {
setExposeNewEntities,
} from "../../../data/expose";
import type { HomeAssistant } from "../../../types";
import "../../../components/voice-assistant-brand-icon";
@customElement("cloud-alexa-pref")
export class CloudAlexaPref extends LitElement {
@@ -139,7 +139,7 @@ export class CloudAlexaPref extends LitElement {
</ha-alert>`
: nothing
}
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.cloud.account.alexa.expose_new_entities"
@@ -156,11 +156,11 @@ export class CloudAlexaPref extends LitElement {
.disabled=${this._exposeNew === undefined}
@change=${this._exposeNewToggleChanged}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
${
alexa_registered
? html`
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.cloud.account.alexa.enable_state_reporting"
@@ -176,7 +176,7 @@ export class CloudAlexaPref extends LitElement {
.checked=${alexa_report_state}
@change=${this._reportToggleChanged}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
`
: nothing
}
@@ -262,8 +262,10 @@ export class CloudAlexaPref extends LitElement {
a {
color: var(--primary-color);
}
ha-row-item {
--ha-row-item-padding-inline: 0;
ha-md-list-item {
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-item-overflow: visible;
}
.header-actions {
position: absolute;
@@ -5,14 +5,13 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
import "../../../components/ha-button";
import "../../../components/ha-md-list-item";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/input/ha-input";
import type { HaInput } from "../../../components/input/ha-input";
import "../../../components/item/ha-row-item";
import "../../../components/voice-assistant-brand-icon";
import type { CloudStatusLoggedIn } from "../../../data/cloud";
import { updateCloudPref } from "../../../data/cloud";
import type { ExposeEntitySettings } from "../../../data/expose";
@@ -22,6 +21,7 @@ import {
} from "../../../data/expose";
import type { HomeAssistant } from "../../../types";
import { showSaveSuccessToast } from "../../../util/toast-saved-success";
import "../../../components/voice-assistant-brand-icon";
@customElement("cloud-google-pref")
export class CloudGooglePref extends LitElement {
@@ -138,7 +138,7 @@ export class CloudGooglePref extends LitElement {
`
: nothing
}
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.cloud.account.google.expose_new_entities"
@@ -155,7 +155,7 @@ export class CloudGooglePref extends LitElement {
.disabled=${this._exposeNew === undefined}
@change=${this._exposeNewToggleChanged}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
${
google_registered
? html`
@@ -183,7 +183,7 @@ export class CloudGooglePref extends LitElement {
`
: nothing
}
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass!.localize(
"ui.panel.config.cloud.account.google.enable_state_reporting"
@@ -199,9 +199,9 @@ export class CloudGooglePref extends LitElement {
.checked=${google_report_state}
@change=${this._reportToggleChanged}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.config.cloud.account.google.security_devices"
@@ -213,7 +213,7 @@ export class CloudGooglePref extends LitElement {
)}</span
>
<ha-switch slot="end"></ha-switch>
</ha-row-item>
</ha-md-list-item>
<ha-input
id="google_secure_devices_pin"
@@ -344,8 +344,10 @@ export class CloudGooglePref extends LitElement {
direction: var(--direction);
color: var(--secondary-text-color);
}
ha-row-item {
--ha-row-item-padding-inline: 0;
ha-md-list-item {
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-item-overflow: visible;
}
ha-input {
width: 250px;
@@ -17,8 +17,8 @@ import {
import "../../../components/ha-alert";
import "../../../components/ha-aliases-editor";
import "../../../components/ha-checkbox";
import "../../../components/ha-md-list-item";
import "../../../components/ha-switch";
import "../../../components/item/ha-row-item";
import "../../../components/voice-assistant-brand-icon";
import { fetchCloudAlexaEntity } from "../../../data/alexa";
import type { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud";
@@ -190,7 +190,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
const exposedToAssist = this.exposed.conversation;
return html`
<ha-row-item>
<ha-md-list-item>
<h3 slot="headline">
${this.hass.localize("ui.dialogs.voice-settings.expose_header")}
</h3>
@@ -200,7 +200,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
.assistants=${uiAssistants}
.checked=${anyExposed}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
${
anyExposed
? showAssistants.map((key) => {
@@ -224,7 +224,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
this._googleEntity?.might_2fa;
return html`
<ha-row-item>
<ha-md-list-item>
<voice-assistant-brand-icon
slot="start"
.voiceAssistantId=${key}
@@ -274,7 +274,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
.disabled=${manualConfig || (!exposed && !supported)}
.checked=${exposed}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
`;
})
: nothing
@@ -321,7 +321,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
)}
</ha-alert>`
: html`
<ha-row-item>
<ha-md-list-item>
<span slot="headline">
${
this.hass.states[this.entityId]
@@ -339,7 +339,7 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
.checked=${(this._aliases ?? this.entry.aliases).includes(null)}
@change=${this._toggleEntityNameAlias}
></ha-switch>
</ha-row-item>
</ha-md-list-item>
<ha-aliases-editor
.aliases=${(this._aliases ?? this.entry.aliases).filter(
(a): a is string => a !== null
@@ -445,8 +445,10 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
margin: 32px;
margin-top: 0;
}
ha-row-item {
--ha-row-item-padding-inline: 0;
ha-md-list-item {
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-item-overflow: visible;
}
img {
height: 32px;
@@ -12,11 +12,11 @@ import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import "../../../../components/ha-grid-size-picker";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-slider";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-switch";
import "../../../../components/ha-yaml-editor";
import "../../../../components/item/ha-row-item";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import { haStyle } from "../../../../resources/styles";
@@ -145,7 +145,7 @@ export class HuiCardLayoutEditor extends LitElement {
.columnMax=${gridOptions.max_columns}
.step=${this._preciseMode ? 1 : GRID_COLUMN_MULTIPLIER}
></ha-grid-size-picker>
<ha-row-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.layout.auto_height"
@@ -162,8 +162,8 @@ export class HuiCardLayoutEditor extends LitElement {
.checked=${options.rows === "auto"}
name="auto-height"
></ha-switch>
</ha-row-item>
<ha-row-item>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.layout.full_width"
@@ -180,8 +180,8 @@ export class HuiCardLayoutEditor extends LitElement {
.checked=${options.columns === "full"}
name="full-width"
></ha-switch>
</ha-row-item>
<ha-row-item>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline"
>${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.layout.precise_mode"
@@ -198,7 +198,7 @@ export class HuiCardLayoutEditor extends LitElement {
.checked=${this._preciseMode}
name="precise-mode"
></ha-switch>
</ha-row-item>
</ha-md-list-item>
`
}
`;
@@ -4,11 +4,11 @@ import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stringCompare } from "../../../../common/string/compare";
import "../../../../components/ha-switch";
import type { HaSwitch } from "../../../../components/ha-switch";
import "../../../../components/item/ha-list-item-base";
import "../../../../components/list/ha-list-base";
import "../../../../components/user/ha-user-badge";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-switch";
import type {
LovelaceViewConfig,
ShowViewConfig,
@@ -66,10 +66,10 @@ export class HuiViewVisibilityEditor extends LitElement {
"ui.panel.lovelace.editor.edit_view.visibility.select_users"
)}
</p>
<ha-list-base>
<ha-md-list>
${this._sortedUsers(this._users).map(
(user) => html`
<ha-list-item-base>
<ha-md-list-item>
<ha-user-badge slot="start" .user=${user}></ha-user-badge>
<span slot="headline">${user.name}</span>
<ha-switch
@@ -78,10 +78,10 @@ export class HuiViewVisibilityEditor extends LitElement {
@change=${this._valChange}
.checked=${this.checkUser(user.id)}
></ha-switch>
</ha-list-item-base>
</ha-md-list-item>
`
)}
</ha-list-base>
</ha-md-list>
`;
}
@@ -135,10 +135,15 @@ export class HuiViewVisibilityEditor extends LitElement {
:host {
display: block;
}
ha-list-item-base {
--ha-row-item-padding-inline: 0;
--ha-row-item-padding-block: var(--ha-space-1);
--ha-row-item-min-height: 48px;
ha-md-list {
padding: 0;
}
ha-md-list-item {
--md-list-item-leading-space: 0;
--md-list-item-trailing-space: 0;
--md-list-item-top-space: var(--ha-space-1);
--md-list-item-bottom-space: var(--ha-space-1);
--md-list-item-one-line-container-height: 48px;
}
`;
}
@@ -0,0 +1,92 @@
import { describe, expect, it } from "vitest";
import type { StatisticsMetaData } from "../../src/data/recorder";
import { getStatisticLabel, StatisticMeanType } from "../../src/data/recorder";
import {
mockArea,
mockDevice,
mockEntity,
} from "../common/entity/context/context-mock";
import { createMockEntityState, createMockHass } from "../fixtures/hass";
const metadata = (
statistic_id: string,
name: string | null
): StatisticsMetaData => ({
statistic_id,
name,
source: "recorder",
statistics_unit_of_measurement: "kWh",
has_sum: true,
mean_type: StatisticMeanType.NONE,
unit_class: "energy",
});
// The friendly name deliberately differs from the composed name, so the
// assertions fail if the label is taken from the attribute again.
const hassWithRegistry = () =>
createMockHass(
{
"sensor.dishwasher_energy": createMockEntityState(
"sensor.dishwasher_energy",
"12",
{ friendly_name: "Smart plug 3 energy" }
),
},
{
entities: {
"sensor.dishwasher_energy": mockEntity({
entity_id: "sensor.dishwasher_energy",
device_id: "device_1",
name: "Energy",
}),
},
devices: {
device_1: mockDevice({
id: "device_1",
name: "Dishwasher",
area_id: "kitchen",
}),
},
areas: { kitchen: mockArea({ area_id: "kitchen", name: "Kitchen" }) },
}
);
describe("getStatisticLabel", () => {
it("composes the name from the registry rather than friendly_name", () => {
expect(
getStatisticLabel(
hassWithRegistry(),
"sensor.dishwasher_energy",
metadata("sensor.dishwasher_energy", "Recorder name")
)
).toBe("Dishwasher Energy");
});
// External statistics have no entity, so there is no registry context to
// compose a name from and the recorder metadata is all we have.
it("uses the metadata name when there is no entity", () => {
expect(
getStatisticLabel(
createMockHass(),
"energy:solar_production",
metadata("energy:solar_production", "Solar production")
)
).toBe("Solar production");
});
it("falls back to the statistic id when metadata has no name", () => {
expect(
getStatisticLabel(
createMockHass(),
"energy:solar_production",
metadata("energy:solar_production", null)
)
).toBe("energy:solar_production");
});
it("falls back to the statistic id when there is no metadata", () => {
expect(
getStatisticLabel(createMockHass(), "energy:solar_production", undefined)
).toBe("energy:solar_production");
});
});
+15 -15
View File
@@ -1,15 +1,4 @@
import { expect, test } from "@playwright/test";
import {
activateDemoSidebarPanel,
DEMO_THEME_STORAGE_KEY,
demoCardSelector,
expectDemoDarkMode,
expectStoredDemoTheme,
loadDemo,
moreInfoCardSelector,
openDemoSidebar,
reloadDemo,
} from "./demo/helpers";
import {
expectNoPageErrors,
NAVIGATION_TIMEOUT,
@@ -18,6 +7,17 @@ import {
SHELL_TIMEOUT,
trackPageErrors,
} from "./helpers";
import {
DEMO_THEME_STORAGE_KEY,
activateDemoSidebarPanel,
demoCardSelector,
expectDemoDarkMode,
expectStoredDemoTheme,
loadDemo,
moreInfoCardSelector,
openDemoSidebar,
reloadDemo,
} from "./demo/helpers";
test.describe("Home Assistant Demo", () => {
let pageErrors: ReturnType<typeof trackPageErrors>;
@@ -82,10 +82,10 @@ test.describe("Home Assistant Demo", () => {
// Adapters come from the config entries, connections and advertisements
// from the subscriptions. A count of zero means the data never arrived.
const rows = dashboard.locator("ha-list-item-button div[slot='headline']");
const rows = dashboard.locator("ha-md-list-item div[slot='headline']");
await expect(rows).toHaveCount(3, { timeout: PANEL_TIMEOUT });
// Asserted over the list as a whole so a single empty count still fails.
await expect(dashboard.locator("ha-list-nav")).not.toHaveText(/\b0\b/, {
await expect(dashboard.locator("ha-md-list")).not.toHaveText(/\b0\b/, {
timeout: QUICK_TIMEOUT,
});
@@ -96,7 +96,7 @@ test.describe("Home Assistant Demo", () => {
await loadDemo(page, "/#/config/bluetooth/adapter-info");
const adapters = page.locator(
"bluetooth-adapter-info-page ha-list-item-button"
"bluetooth-adapter-info-page ha-md-list-item"
);
await expect(adapters).toHaveCount(3, { timeout: PANEL_TIMEOUT });
await expect(adapters.locator("ha-icon-button")).toHaveCount(1, {
@@ -120,7 +120,7 @@ test.describe("Home Assistant Demo", () => {
});
// The device and entity counts come from the registries; a zero means the
// fixtures did not reach them.
await expect(dashboard.locator("ha-list-nav").first()).not.toHaveText(
await expect(dashboard.locator("ha-md-list").first()).not.toHaveText(
/\b0\b/,
{ timeout: QUICK_TIMEOUT }
);
+10 -10
View File
@@ -2798,12 +2798,12 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/icu-messageformat-parser@npm:3.5.18":
version: 3.5.18
resolution: "@formatjs/icu-messageformat-parser@npm:3.5.18"
"@formatjs/icu-messageformat-parser@npm:3.5.17":
version: 3.5.17
resolution: "@formatjs/icu-messageformat-parser@npm:3.5.17"
dependencies:
"@formatjs/icu-skeleton-parser": "npm:2.1.11"
checksum: 10/fc2215a16571fcff3eb9f446878ef7649d40064a79648352ab79f68f4b48ce7b196125495eec82caa02edec78a9a094d8fd6fd5db0abeb353b95afb62672c2a2
checksum: 10/cbb9daf23f65e4ef3697eae3be4c6888eda942fcde18929dfc6e96ef6c45052409d20340ece13476eeadd062e222809c46a2f26e3b453c9cbd4979bb6c0b0ae5
languageName: node
linkType: hard
@@ -10472,7 +10472,7 @@ __metadata:
html-minifier-terser: "npm:7.2.0"
husky: "npm:9.1.7"
idb-keyval: "npm:6.3.0"
intl-messageformat: "npm:11.2.15"
intl-messageformat: "npm:11.2.14"
js-yaml: "npm:5.4.1"
jsdom: "npm:30.0.1"
jszip: "npm:3.10.2"
@@ -10879,13 +10879,13 @@ __metadata:
languageName: node
linkType: hard
"intl-messageformat@npm:11.2.15":
version: 11.2.15
resolution: "intl-messageformat@npm:11.2.15"
"intl-messageformat@npm:11.2.14":
version: 11.2.14
resolution: "intl-messageformat@npm:11.2.14"
dependencies:
"@formatjs/fast-memoize": "npm:3.1.7"
"@formatjs/icu-messageformat-parser": "npm:3.5.18"
checksum: 10/71d1448964e685c821b3680a89668aae3fe92f6b7774842ea5eb08830417846d4fcbcaf691930e4da1ceb2cc414eeab468c7e3dd4eb530a38ffed91530468ac5
"@formatjs/icu-messageformat-parser": "npm:3.5.17"
checksum: 10/2720155c42bfd99a00d41f7bc9e2f5c2c83c2b6cb2353006b7d8e736a0f1ed20d0147406c1aeba0748731a0ce588c67304b52eb2fe92b42b928bf1ef6bffdcdf
languageName: node
linkType: hard