1
0
mirror of https://github.com/home-assistant/frontend.git synced 2025-12-24 20:55:49 +00:00

Migrate ha-button-menu to ha-dropdown in areas, dashboard, cloud, and network panels

Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-05 11:33:16 +00:00
parent dddcd04ce9
commit 950a773f7e
6 changed files with 160 additions and 116 deletions

View File

@@ -16,8 +16,10 @@ import { slugify } from "../../../common/string/slugify";
import { groupBy } from "../../../common/util/group-by";
import { afterNextRender } from "../../../common/util/render-status";
import "../../../components/ha-button";
import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-next";
import "../../../components/ha-list";
@@ -226,32 +228,23 @@ class HaConfigAreaPage extends LitElement {
></ha-icon>`
: nothing}${area.name}`}
>
<ha-button-menu slot="toolbar-icon">
<ha-dropdown slot="toolbar-icon" @wa-select=${this._handleMenuAction}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-list-item
graphic="icon"
.entry=${area}
@click=${this._showSettings}
>
<ha-dropdown-item value="settings" .entry=${area}>
<ha-svg-icon slot="icon" .path=${mdiPencil}></ha-svg-icon>
${this.hass.localize("ui.panel.config.areas.edit_settings")}
<ha-svg-icon slot="graphic" .path=${mdiPencil}> </ha-svg-icon>
</ha-list-item>
</ha-dropdown-item>
<ha-list-item
class="warning"
graphic="icon"
@click=${this._deleteConfirm}
>
<ha-dropdown-item value="delete" variant="danger">
<ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
${this.hass.localize("ui.panel.config.areas.editor.delete")}
<ha-svg-icon class="warning" slot="graphic" .path=${mdiDelete}>
</ha-svg-icon>
</ha-list-item>
</ha-button-menu>
</ha-dropdown-item>
</ha-dropdown>
<div class="container">
<div class="column">
@@ -613,6 +606,20 @@ class HaConfigAreaPage extends LitElement {
this._related = await findRelated(this.hass, "area", this.areaId);
}
private _handleMenuAction(ev: CustomEvent) {
const item = ev.detail.item as HaDropdownItem & {
entry: AreaRegistryEntry;
};
switch (item.value) {
case "settings":
this._openDialog(item.entry);
break;
case "delete":
this._deleteConfirm();
break;
}
}
private _showSettings(ev: MouseEvent) {
const entry: AreaRegistryEntry = (ev.currentTarget! as any).entry;
this._openDialog(entry);

View File

@@ -1,4 +1,4 @@
import type { ActionDetail } from "@material/mwc-list";
import "@home-assistant/webawesome/dist/components/divider/divider";
import {
mdiDelete,
mdiDotsVertical,
@@ -24,10 +24,12 @@ import {
type AreasFloorHierarchy,
} from "../../../common/areas/areas-floor-hierarchy";
import { formatListWithAnds } from "../../../common/string/format-list";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
import "../../../components/ha-fab";
import "../../../components/ha-floor-icon";
import "../../../components/ha-icon-button";
import "../../../components/ha-list-item";
import "../../../components/ha-sortable";
import type { HaSortableOptions } from "../../../components/ha-sortable";
import "../../../components/ha-svg-icon";
@@ -196,44 +198,43 @@ export class HaConfigAreasDashboard extends LitElement {
${floor.name}
</h2>
<div class="actions">
<ha-button-menu
<ha-dropdown
.floor=${floor}
@action=${this._handleFloorAction}
@wa-select=${this._handleFloorAction}
>
<ha-icon-button
slot="trigger"
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-list-item graphic="icon"
><ha-svg-icon
<ha-dropdown-item value="reorder">
<ha-svg-icon
slot="icon"
.path=${mdiSort}
slot="graphic"
></ha-svg-icon
>${this.hass.localize(
></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.areas.picker.reorder"
)}</ha-list-item
>
<li divider role="separator"></li>
<ha-list-item graphic="icon"
><ha-svg-icon
)}
</ha-dropdown-item>
<wa-divider></wa-divider>
<ha-dropdown-item value="edit">
<ha-svg-icon
slot="icon"
.path=${mdiPencil}
slot="graphic"
></ha-svg-icon
>${this.hass.localize(
></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.areas.picker.floor.edit_floor"
)}</ha-list-item
>
<ha-list-item class="warning" graphic="icon"
><ha-svg-icon
class="warning"
)}
</ha-dropdown-item>
<ha-dropdown-item value="delete" variant="danger">
<ha-svg-icon
slot="icon"
.path=${mdiDelete}
slot="graphic"
></ha-svg-icon
>${this.hass.localize(
></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.areas.picker.floor.delete_floor"
)}</ha-list-item
>
</ha-button-menu>
)}
</ha-dropdown-item>
</ha-dropdown>
</div>
</div>
<ha-sortable
@@ -273,23 +274,23 @@ export class HaConfigAreasDashboard extends LitElement {
)}
</h2>
<div class="actions">
<ha-button-menu
@action=${this._handleUnassignedAreasAction}
<ha-dropdown
@wa-select=${this._handleUnassignedAreasAction}
>
<ha-icon-button
slot="trigger"
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-list-item graphic="icon"
><ha-svg-icon
<ha-dropdown-item value="reorder">
<ha-svg-icon
slot="icon"
.path=${mdiSort}
slot="graphic"
></ha-svg-icon
>${this.hass.localize(
></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.areas.picker.reorder"
)}</ha-list-item
>
</ha-button-menu>
)}
</ha-dropdown-item>
</ha-dropdown>
</div>
</div>
<ha-sortable
@@ -533,23 +534,25 @@ export class HaConfigAreasDashboard extends LitElement {
}, time);
}
private _handleFloorAction(ev: CustomEvent<ActionDetail>) {
private _handleFloorAction(ev: CustomEvent) {
const item = ev.detail.item as HaDropdownItem;
const floor = (ev.currentTarget as any).floor;
switch (ev.detail.index) {
case 0:
switch (item.value) {
case "reorder":
this._showReorderDialog();
break;
case 1:
case "edit":
this._editFloor(floor);
break;
case 2:
case "delete":
this._deleteFloor(floor);
break;
}
}
private _handleUnassignedAreasAction(ev: CustomEvent<ActionDetail>) {
if (ev.detail.index === 0) {
private _handleUnassignedAreasAction(ev: CustomEvent) {
const item = ev.detail.item as HaDropdownItem;
if (item.value === "reorder") {
this._showReorderDialog();
}
}

View File

@@ -6,8 +6,10 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import { debounce } from "../../../../common/util/debounce";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
import "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
import "../../../../components/ha-list-item";
import "../../../../components/ha-tip";
import type {
@@ -53,26 +55,26 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
.narrow=${this.narrow}
header="Home Assistant Cloud"
>
<ha-button-menu slot="toolbar-icon" @action=${this._handleMenuAction}>
<ha-dropdown slot="toolbar-icon" @wa-select=${this._handleMenuAction}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-list-item graphic="icon">
<ha-dropdown-item value="reset">
<ha-svg-icon slot="icon" .path=${mdiDeleteForever}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.cloud.account.reset_cloud_data"
)}
<ha-svg-icon slot="graphic" .path=${mdiDeleteForever}></ha-svg-icon>
</ha-list-item>
<ha-list-item graphic="icon">
</ha-dropdown-item>
<ha-dropdown-item value="download">
<ha-svg-icon slot="icon" .path=${mdiDownload}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.cloud.account.download_support_package"
)}
<ha-svg-icon slot="graphic" .path=${mdiDownload}></ha-svg-icon>
</ha-list-item>
</ha-button-menu>
</ha-dropdown-item>
</ha-dropdown>
<div class="content">
<ha-config-section .isWide=${this.isWide}>
<span slot="header">Home Assistant Cloud</span>
@@ -297,13 +299,15 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
fireEvent(this, "ha-refresh-cloud-status");
}
private _handleMenuAction(ev) {
switch (ev.detail.index) {
case 0:
private _handleMenuAction(ev: CustomEvent) {
const item = ev.detail.item as HaDropdownItem;
switch (item.value) {
case "reset":
this._deleteCloudData();
break;
case 1:
case "download":
this._downloadSupportPackage();
break;
}
}

View File

@@ -5,8 +5,10 @@ import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { navigate } from "../../../../common/navigate";
import "../../../../components/ha-alert";
import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
import "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../../../components/ha-dropdown-item";
import "../../../../components/ha-icon-next";
import "../../../../components/ha-list";
import "../../../../components/ha-list-item";
@@ -44,26 +46,26 @@ export class CloudLoginPanel extends LitElement {
.narrow=${this.narrow}
header="Home Assistant Cloud"
>
<ha-button-menu slot="toolbar-icon" @action=${this._handleMenuAction}>
<ha-dropdown slot="toolbar-icon" @wa-select=${this._handleMenuAction}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-list-item graphic="icon">
<ha-dropdown-item value="reset">
<ha-svg-icon slot="icon" .path=${mdiDeleteForever}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.cloud.account.reset_cloud_data"
)}
<ha-svg-icon slot="graphic" .path=${mdiDeleteForever}></ha-svg-icon>
</ha-list-item>
<ha-list-item graphic="icon">
</ha-dropdown-item>
<ha-dropdown-item value="download">
<ha-svg-icon slot="icon" .path=${mdiDownload}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.cloud.account.download_support_package"
)}
<ha-svg-icon slot="graphic" .path=${mdiDownload}></ha-svg-icon>
</ha-list-item>
</ha-button-menu>
</ha-dropdown-item>
</ha-dropdown>
<div class="content">
<ha-config-section .isWide=${this.isWide}>
<span slot="header">Home Assistant Cloud</span>
@@ -164,13 +166,15 @@ export class CloudLoginPanel extends LitElement {
fireEvent(this, "flash-message-changed", { value: "" });
}
private _handleMenuAction(ev) {
switch (ev.detail.index) {
case 0:
private _handleMenuAction(ev: CustomEvent) {
const item = ev.detail.item as HaDropdownItem;
switch (item.value) {
case "reset":
this._deleteCloudData();
break;
case 1:
case "download":
this._downloadSupportPackage();
break;
}
}

View File

@@ -1,4 +1,3 @@
import type { ActionDetail } from "@material/mwc-list";
import {
mdiCloudLock,
mdiDotsVertical,
@@ -13,11 +12,12 @@ import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import "../../../components/chips/ha-assist-chip";
import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-next";
import "../../../components/ha-list-item";
import "../../../components/ha-menu-button";
import "../../../components/ha-svg-icon";
import "../../../components/ha-tip";
@@ -226,25 +226,25 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
.path=${mdiMagnify}
@click=${this._showQuickBar}
></ha-icon-button>
<ha-button-menu slot="actionItems" @action=${this._handleMenuAction}>
<ha-dropdown slot="actionItems" @wa-select=${this._handleMenuAction}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-list-item graphic="icon">
<ha-dropdown-item value="check_updates">
<ha-svg-icon slot="icon" .path=${mdiRefresh}></ha-svg-icon>
${this.hass.localize("ui.panel.config.updates.check_updates")}
<ha-svg-icon slot="graphic" .path=${mdiRefresh}></ha-svg-icon>
</ha-list-item>
</ha-dropdown-item>
<ha-list-item graphic="icon">
<ha-dropdown-item value="restart">
<ha-svg-icon slot="icon" .path=${mdiPower}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.system_dashboard.restart_homeassistant"
)}
<ha-svg-icon slot="graphic" .path=${mdiPower}></ha-svg-icon>
</ha-list-item>
</ha-button-menu>
</ha-dropdown-item>
</ha-dropdown>
<ha-config-section
.narrow=${this.narrow}
@@ -371,12 +371,13 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
});
}
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
private async _handleMenuAction(ev: CustomEvent) {
const item = ev.detail.item as HaDropdownItem;
switch (item.value) {
case "check_updates":
checkForEntityUpdates(this, this.hass);
break;
case 1:
case "restart":
showRestartDialog(this);
break;
}

View File

@@ -4,8 +4,10 @@ import { customElement, property, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
import "../../../components/ha-expansion-panel";
import "../../../components/ha-formfield";
import "../../../components/ha-icon-button";
@@ -500,13 +502,12 @@ export class HassioNetwork extends LitElement {
`
)}
</div>
<ha-button-menu
@opened=${this._handleDNSMenuOpened}
@closed=${this._handleDNSMenuClosed}
<ha-dropdown
@wa-show=${this._handleDNSMenuOpened}
@wa-hide=${this._handleDNSMenuClosed}
@wa-select=${this._handleDNSMenuSelect}
.version=${version}
class="add-nameserver"
appearance="filled"
size="small"
>
<ha-button appearance="filled" size="small" slot="trigger">
${this.hass.localize(
@@ -519,21 +520,21 @@ export class HassioNetwork extends LitElement {
</ha-button>
${Object.entries(PREDEFINED_DNS[version]).map(
([name, addresses]) => html`
<ha-list-item
@click=${this._addPredefinedDNS}
<ha-dropdown-item
value="predefined"
.version=${version}
.addresses=${addresses}
>
${name}
</ha-list-item>
</ha-dropdown-item>
`
)}
<ha-list-item @click=${this._addCustomDNS} .version=${version}>
<ha-dropdown-item value="custom" .version=${version}>
${this.hass.localize(
"ui.panel.config.network.supervisor.custom_dns"
)}
</ha-list-item>
</ha-button-menu>
</ha-dropdown-item>
</ha-dropdown>
`
: nothing}
</ha-expansion-panel>
@@ -747,6 +748,30 @@ export class HassioNetwork extends LitElement {
this._dnsMenuOpen = false;
}
private _handleDNSMenuSelect(ev: CustomEvent) {
const item = ev.detail.item as HaDropdownItem & {
version: "ipv4" | "ipv6";
addresses?: string[];
};
const version = item.version;
if (item.value === "predefined" && item.addresses) {
if (!this._interface![version]!.nameservers) {
this._interface![version]!.nameservers = [];
}
this._interface![version]!.nameservers!.push(...item.addresses);
this._dirty = true;
this.requestUpdate("_interface");
} else if (item.value === "custom") {
if (!this._interface![version]!.nameservers) {
this._interface![version]!.nameservers = [];
}
this._interface![version]!.nameservers!.push("");
this._dirty = true;
this.requestUpdate("_interface");
}
}
private _addPredefinedDNS(ev: Event) {
const source = ev.target as any;
const version = source.version as "ipv4" | "ipv6";