Migrate ha-button-menu to ha-dropdown in 4 files (#28300)

Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Wendelin <w@pe8.at>
Co-authored-by: uptimeZERO_ <pavilionsahota@gmail.com>
This commit is contained in:
Copilot
2025-12-03 16:43:07 +00:00
committed by GitHub
parent 844d53a0ba
commit c43d41053b
4 changed files with 144 additions and 167 deletions

View File

@@ -1,5 +1,4 @@
import type { List } from "@material/mwc-list/mwc-list"; import type { List } from "@material/mwc-list/mwc-list";
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import { import {
mdiClock, mdiClock,
mdiDelete, mdiDelete,
@@ -18,15 +17,16 @@ import { classMap } from "lit/directives/class-map";
import { repeat } from "lit/directives/repeat"; import { repeat } from "lit/directives/repeat";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { supportsFeature } from "../../../common/entity/supports-feature"; import { supportsFeature } from "../../../common/entity/supports-feature";
import { caseInsensitiveStringCompare } from "../../../common/string/compare"; import { caseInsensitiveStringCompare } from "../../../common/string/compare";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-check-list-item"; import "../../../components/ha-check-list-item";
import "../../../components/ha-checkbox"; import "../../../components/ha-checkbox";
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-button";
import "../../../components/ha-list"; import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/ha-markdown-element"; import "../../../components/ha-markdown-element";
import "../../../components/ha-relative-time"; import "../../../components/ha-relative-time";
import "../../../components/ha-select"; import "../../../components/ha-select";
@@ -378,28 +378,29 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
${this._todoListSupportsFeature( ${this._todoListSupportsFeature(
TodoListEntityFeature.DELETE_TODO_ITEM TodoListEntityFeature.DELETE_TODO_ITEM
) )
? html`<ha-button-menu ? html`<ha-dropdown
@closed=${stopPropagation} @wa-select=${this._handleCompletedMenuSelect}
fixed placement="bottom-end"
@action=${this._handleCompletedMenuAction}
> >
<ha-icon-button <ha-icon-button
slot="trigger" slot="trigger"
.path=${mdiDotsVertical} .path=${mdiDotsVertical}
></ha-icon-button> ></ha-icon-button>
<ha-list-item graphic="icon" class="warning"> <ha-dropdown-item
value="clear"
variant="danger"
>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.cards.todo-list.clear_items" "ui.panel.lovelace.cards.todo-list.clear_items"
)} )}
<ha-svg-icon <ha-svg-icon
class="warning" class="warning"
slot="graphic" slot="icon"
.path=${mdiDeleteSweep} .path=${mdiDeleteSweep}
.disabled=${unavailable}
> >
</ha-svg-icon> </ha-svg-icon>
</ha-list-item> </ha-dropdown-item>
</ha-button-menu>` </ha-dropdown>`
: nothing} : nothing}
</div>` </div>`
: nothing} : nothing}
@@ -413,33 +414,27 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
`; `;
} }
private _renderMenu(config: TodoListCardConfig, unavailable: boolean) { private _renderMenu(config: TodoListCardConfig, _unavailable: boolean) {
return (!config.display_order || return (!config.display_order ||
config.display_order === TodoSortMode.NONE) && config.display_order === TodoSortMode.NONE) &&
this._todoListSupportsFeature(TodoListEntityFeature.MOVE_TODO_ITEM) this._todoListSupportsFeature(TodoListEntityFeature.MOVE_TODO_ITEM)
? html`<ha-button-menu ? html`<ha-dropdown
@closed=${stopPropagation} @wa-select=${this._handlePrimaryMenuSelect}
fixed placement="bottom-end"
@action=${this._handlePrimaryMenuAction}
> >
<ha-icon-button <ha-icon-button
slot="trigger" slot="trigger"
.path=${mdiDotsVertical} .path=${mdiDotsVertical}
></ha-icon-button> ></ha-icon-button>
<ha-list-item graphic="icon"> <ha-dropdown-item value="reorder">
${this.hass!.localize( ${this.hass!.localize(
this._reordering this._reordering
? "ui.panel.lovelace.cards.todo-list.exit_reorder_items" ? "ui.panel.lovelace.cards.todo-list.exit_reorder_items"
: "ui.panel.lovelace.cards.todo-list.reorder_items" : "ui.panel.lovelace.cards.todo-list.reorder_items"
)} )}
<ha-svg-icon <ha-svg-icon slot="icon" .path=${mdiSort}> </ha-svg-icon>
slot="graphic" </ha-dropdown-item>
.path=${mdiSort} </ha-dropdown>`
.disabled=${unavailable}
>
</ha-svg-icon>
</ha-list-item>
</ha-button-menu>`
: nothing; : nothing;
} }
@@ -641,11 +636,11 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
} }
} }
private _handleCompletedMenuAction(ev: CustomEvent<ActionDetail>) { private _handleCompletedMenuSelect(
switch (ev.detail.index) { ev: CustomEvent<{ item: HaDropdownItem }>
case 0: ) {
this._clearCompletedItems(); if (ev.detail?.item?.value === "clear") {
break; this._clearCompletedItems();
} }
} }
@@ -704,11 +699,9 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
} }
} }
private _handlePrimaryMenuAction(ev: CustomEvent<ActionDetail>) { private _handlePrimaryMenuSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
switch (ev.detail.index) { if (ev.detail?.item?.value === "reorder") {
case 0: this._toggleReorder();
this._toggleReorder();
break;
} }
} }

View File

@@ -1,3 +1,4 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import { import {
mdiContentCopy, mdiContentCopy,
mdiContentCut, mdiContentCut,
@@ -12,9 +13,10 @@ import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button-menu"; 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-button";
import "../../../components/ha-list-item";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
@@ -41,9 +43,6 @@ export class HuiCardEditMode extends LitElement {
@property({ type: Boolean, attribute: "no-move" }) @property({ type: Boolean, attribute: "no-move" })
public noMove = false; public noMove = false;
@state()
public _menuOpened = false;
@state() @state()
public _hover = false; public _hover = false;
@@ -91,8 +90,7 @@ export class HuiCardEditMode extends LitElement {
}; };
protected render(): TemplateResult { protected render(): TemplateResult {
const showOverlay = const showOverlay = (this._hover || this._focused) && !this.hiddenOverlay;
(this._hover || this._menuOpened || this._focused) && !this.hiddenOverlay;
return html` return html`
<div class="card-wrapper" inert><slot></slot></div> <div class="card-wrapper" inert><slot></slot></div>
@@ -115,107 +113,71 @@ export class HuiCardEditMode extends LitElement {
<ha-svg-icon .path=${mdiPencil}> </ha-svg-icon> <ha-svg-icon .path=${mdiPencil}> </ha-svg-icon>
</div> </div>
`} `}
<ha-button-menu <ha-dropdown
class="more" class="more"
corner="BOTTOM_END" placement="bottom-end"
menu-corner="END" @wa-select=${this._handleDropdownSelect}
.path=${[this.path!]}
@action=${this._handleAction}
@opened=${this._handleOpened}
@closed=${this._handleClosed}
> >
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}> <ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
</ha-icon-button> </ha-icon-button>
${this.noEdit ${this.noEdit
? nothing ? nothing
: html` : html`
<ha-list-item <ha-dropdown-item value="edit">
graphic="icon" <ha-svg-icon slot="icon" .path=${mdiPencil}></ha-svg-icon>
@click=${this._handleAction}
.action=${"edit"}
>
<ha-svg-icon slot="graphic" .path=${mdiPencil}></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.edit" "ui.panel.lovelace.editor.edit_card.edit"
)} )}
</ha-list-item> </ha-dropdown-item>
`} `}
${this.noDuplicate ${this.noDuplicate
? nothing ? nothing
: html` : html`
<ha-list-item <ha-dropdown-item value="duplicate">
graphic="icon"
@click=${this._handleAction}
.action=${"duplicate"}
>
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="icon"
.path=${mdiPlusCircleMultipleOutline} .path=${mdiPlusCircleMultipleOutline}
></ha-svg-icon> ></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.duplicate" "ui.panel.lovelace.editor.edit_card.duplicate"
)} )}
</ha-list-item> </ha-dropdown-item>
`} `}
${this.noMove ${this.noMove
? nothing ? nothing
: html` : html`
<ha-list-item <ha-dropdown-item value="copy">
graphic="icon"
@click=${this._handleAction}
.action=${"copy"}
>
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="icon"
.path=${mdiContentCopy} .path=${mdiContentCopy}
></ha-svg-icon> ></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.copy" "ui.panel.lovelace.editor.edit_card.copy"
)} )}
</ha-list-item> </ha-dropdown-item>
<ha-list-item <ha-dropdown-item value="cut">
graphic="icon" <ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
@click=${this._handleAction}
.action=${"cut"}
>
<ha-svg-icon
slot="graphic"
.path=${mdiContentCut}
></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.cut" "ui.panel.lovelace.editor.edit_card.cut"
)} )}
</ha-list-item> </ha-dropdown-item>
`} `}
${this.noDuplicate && this.noEdit && this.noMove ${this.noDuplicate && this.noEdit && this.noMove
? nothing ? nothing
: html`<li divider role="separator"></li>`} : html`<wa-divider></wa-divider>`}
<ha-list-item <ha-dropdown-item value="delete" variant="danger">
graphic="icon"
class="warning"
@click=${this._handleAction}
.action=${"delete"}
>
${this.hass.localize("ui.panel.lovelace.editor.edit_card.delete")} ${this.hass.localize("ui.panel.lovelace.editor.edit_card.delete")}
<ha-svg-icon <ha-svg-icon
class="warning" class="warning"
slot="graphic" slot="icon"
.path=${mdiDelete} .path=${mdiDelete}
></ha-svg-icon> ></ha-svg-icon>
</ha-list-item> </ha-dropdown-item>
</ha-button-menu> </ha-dropdown>
</div> </div>
`; `;
} }
private _handleOpened() {
this._menuOpened = true;
}
private _handleClosed() {
this._menuOpened = false;
}
private _handleOverlayClick(ev): void { private _handleOverlayClick(ev): void {
if (ev.defaultPrevented) { if (ev.defaultPrevented) {
return; return;
@@ -228,8 +190,14 @@ export class HuiCardEditMode extends LitElement {
this._editCard(); this._editCard();
} }
private _handleAction(ev) { private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
switch (ev.currentTarget.action) { const action = ev.detail?.item?.value;
if (!action) {
return;
}
switch (action) {
case "edit": case "edit":
this._editCard(); this._editCard();
break; break;
@@ -330,14 +298,12 @@ export class HuiCardEditMode extends LitElement {
background: var(--secondary-background-color); background: var(--secondary-background-color);
--mdc-icon-size: 20px; --mdc-icon-size: 20px;
} }
.more { .more ha-icon-button {
position: absolute; position: absolute;
right: -6px; right: -6px;
top: -6px; top: -6px;
inset-inline-end: -6px; inset-inline-end: -6px;
inset-inline-start: initial; inset-inline-start: initial;
}
.more ha-icon-button {
cursor: pointer; cursor: pointer;
border-radius: var(--ha-border-radius-circle); border-radius: var(--ha-border-radius-circle);
background: var(--secondary-background-color); background: var(--secondary-background-color);

View File

@@ -1,4 +1,4 @@
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@home-assistant/webawesome/dist/components/divider/divider";
import { import {
mdiContentCopy, mdiContentCopy,
mdiContentCut, mdiContentCut,
@@ -15,10 +15,11 @@ import { customElement, property, queryAssignedElements } from "lit/decorators";
import { storage } from "../../../common/decorators/storage"; import { storage } from "../../../common/decorators/storage";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button"; import "../../../components/ha-button";
import "../../../components/ha-button-menu";
import "../../../components/ha-card"; 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-button";
import "../../../components/ha-list-item";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import { saveConfig } from "../../../data/lovelace/config/types"; import { saveConfig } from "../../../data/lovelace/config/types";
import { isStrategyView } from "../../../data/lovelace/config/view"; import { isStrategyView } from "../../../data/lovelace/config/view";
@@ -132,7 +133,10 @@ export class HuiCardOptions extends LitElement {
></ha-icon-button> ></ha-icon-button>
` `
: nothing} : nothing}
<ha-button-menu @action=${this._handleAction}> <ha-dropdown
@wa-select=${this._handleDropdownSelect}
placement="bottom-end"
>
<ha-icon-button <ha-icon-button
slot="trigger" slot="trigger"
.label=${this.hass!.localize( .label=${this.hass!.localize(
@@ -140,52 +144,46 @@ export class HuiCardOptions extends LitElement {
)} )}
.path=${mdiDotsVertical} .path=${mdiDotsVertical}
></ha-icon-button> ></ha-icon-button>
<ha-list-item graphic="icon"> <ha-dropdown-item value="move">
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="icon"
.path=${mdiFileMoveOutline} .path=${mdiFileMoveOutline}
></ha-svg-icon> ></ha-svg-icon>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.move" "ui.panel.lovelace.editor.edit_card.move"
)} )}
</ha-list-item> </ha-dropdown-item>
<ha-list-item graphic="icon"> <ha-dropdown-item value="duplicate">
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="icon"
.path=${mdiPlusCircleMultipleOutline} .path=${mdiPlusCircleMultipleOutline}
></ha-svg-icon> ></ha-svg-icon>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.duplicate" "ui.panel.lovelace.editor.edit_card.duplicate"
)} )}
</ha-list-item> </ha-dropdown-item>
<ha-list-item graphic="icon"> <ha-dropdown-item value="copy">
<ha-svg-icon <ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
slot="graphic"
.path=${mdiContentCopy}
></ha-svg-icon>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.copy" "ui.panel.lovelace.editor.edit_card.copy"
)} )}
</ha-list-item> </ha-dropdown-item>
<ha-list-item graphic="icon"> <ha-dropdown-item value="cut">
<ha-svg-icon <ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
slot="graphic"
.path=${mdiContentCut}
></ha-svg-icon>
${this.hass!.localize("ui.panel.lovelace.editor.edit_card.cut")} ${this.hass!.localize("ui.panel.lovelace.editor.edit_card.cut")}
</ha-list-item> </ha-dropdown-item>
<li divider role="separator"></li> <wa-divider></wa-divider>
<ha-list-item class="warning" graphic="icon"> <ha-dropdown-item value="delete" variant="danger">
<ha-svg-icon <ha-svg-icon
class="warning" class="warning"
slot="graphic" slot="icon"
.path=${mdiDelete} .path=${mdiDelete}
></ha-svg-icon> ></ha-svg-icon>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.delete" "ui.panel.lovelace.editor.edit_card.delete"
)} )}
</ha-list-item> </ha-dropdown-item>
</ha-button-menu> </ha-dropdown>
</div> </div>
</div> </div>
</ha-card> </ha-card>
@@ -244,30 +242,31 @@ export class HuiCardOptions extends LitElement {
ha-icon-button.move-arrow[disabled] { ha-icon-button.move-arrow[disabled] {
color: var(--disabled-text-color); color: var(--disabled-text-color);
} }
ha-list-item {
cursor: pointer;
white-space: nowrap;
}
`, `,
]; ];
} }
private _handleAction(ev: CustomEvent<ActionDetail>) { private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
switch (ev.detail.index) { const action = ev.detail?.item?.value;
case 0:
if (!action) {
return;
}
switch (action) {
case "move":
this._moveCard(); this._moveCard();
break; break;
case 1: case "duplicate":
this._duplicateCard(); this._duplicateCard();
break; break;
case 2: case "copy":
this._copyCard(); this._copyCard();
break; break;
case 3: case "cut":
this._cutCard(); this._cutCard();
break; break;
case 4: case "delete":
this._deleteCard(); this._deleteCard();
break; break;
} }

View File

@@ -1,3 +1,4 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import { ResizeController } from "@lit-labs/observers/resize-controller"; import { ResizeController } from "@lit-labs/observers/resize-controller";
import { import {
mdiChevronDown, mdiChevronDown,
@@ -23,6 +24,10 @@ import {
extractSearchParam, extractSearchParam,
} from "../../common/url/search-params"; } from "../../common/url/search-params";
import "../../components/ha-button"; import "../../components/ha-button";
import "../../components/ha-button-menu";
import "../../components/ha-dropdown";
import "../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../components/ha-dropdown-item";
import "../../components/ha-fab"; import "../../components/ha-fab";
import "../../components/ha-icon-button"; import "../../components/ha-icon-button";
import "../../components/ha-list"; import "../../components/ha-list";
@@ -222,47 +227,41 @@ class PanelTodo extends LitElement {
${this.hass.localize("ui.panel.todo.create_list")} ${this.hass.localize("ui.panel.todo.create_list")}
</ha-list-item>` </ha-list-item>`
: nothing} : nothing}
<ha-button-menu slot="actionItems"> <ha-dropdown
slot="actionItems"
@wa-select=${this._handleDropdownSelect}
>
<ha-icon-button <ha-icon-button
slot="trigger" slot="trigger"
.label=${""} .label=${""}
.path=${mdiDotsVertical} .path=${mdiDotsVertical}
></ha-icon-button> ></ha-icon-button>
${this._conversation(this.hass.config.components) ${this._conversation(this.hass.config.components)
? html`<ha-list-item ? html`<ha-dropdown-item value="info" .disabled=${!this._entityId}>
graphic="icon" <ha-svg-icon .path=${mdiInformationOutline} slot="icon">
@click=${this._showMoreInfoDialog}
.disabled=${!this._entityId}
>
<ha-svg-icon .path=${mdiInformationOutline} slot="graphic">
</ha-svg-icon> </ha-svg-icon>
${this.hass.localize("ui.panel.todo.information")} ${this.hass.localize("ui.panel.todo.information")}
</ha-list-item>` </ha-dropdown-item>`
: nothing} : nothing}
<li divider role="separator"></li> <wa-divider></wa-divider>
<ha-list-item graphic="icon" @click=${this._showVoiceCommandDialog}> <ha-dropdown-item value="assist">
<ha-svg-icon .path=${mdiCommentProcessingOutline} slot="graphic"> <ha-svg-icon .path=${mdiCommentProcessingOutline} slot="icon">
</ha-svg-icon> </ha-svg-icon>
${this.hass.localize("ui.panel.todo.assist")} ${this.hass.localize("ui.panel.todo.assist")}
</ha-list-item> </ha-dropdown-item>
${entityRegistryEntry?.platform === "local_todo" ${entityRegistryEntry?.platform === "local_todo"
? html` <li divider role="separator"></li> ? html` <wa-divider></wa-divider>
<ha-list-item <ha-dropdown-item
graphic="icon" value="delete"
@click=${this._deleteList} variant="danger"
class="warning"
.disabled=${!this._entityId} .disabled=${!this._entityId}
> >
<ha-svg-icon <ha-svg-icon .path=${mdiDelete} slot="icon" class="warning">
.path=${mdiDelete}
slot="graphic"
class="warning"
>
</ha-svg-icon> </ha-svg-icon>
${this.hass.localize("ui.panel.todo.delete_list")} ${this.hass.localize("ui.panel.todo.delete_list")}
</ha-list-item>` </ha-dropdown-item>`
: nothing} : nothing}
</ha-button-menu> </ha-dropdown>
<div id="columns"> <div id="columns">
<div class="column"> <div class="column">
${this._entityId ${this._entityId
@@ -363,6 +362,26 @@ class PanelTodo extends LitElement {
showTodoItemEditDialog(this, { entity: this._entityId! }); showTodoItemEditDialog(this, { entity: this._entityId! });
} }
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
const action = ev.detail?.item?.value;
if (!action) {
return;
}
switch (action) {
case "info":
this._showMoreInfoDialog();
break;
case "assist":
this._showVoiceCommandDialog();
break;
case "delete":
this._deleteList();
break;
}
}
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return [
haStyle, haStyle,