Fix mwc-list/menu actions (#6442)

* Fix mwc-list/menu actions

Fix double actions when using request-selected

* Update ha-button-menu.ts

* Automation menu styling

* Update src/panels/lovelace/hui-root.ts

Co-authored-by: Zack Arnett <arnett.zackary@gmail.com>

* Move

Co-authored-by: Zack Arnett <arnett.zackary@gmail.com>
This commit is contained in:
Bram Kragten 2020-07-21 23:22:19 +02:00 committed by GitHub
parent e08c10315e
commit 4404a1173b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 353 additions and 227 deletions

View File

@ -25,6 +25,7 @@ import { HomeAssistant, Route } from "../../../src/types";
import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories";
import { supervisorTabs } from "../hassio-tabs";
import "./hassio-addon-repository";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
if (a.slug === "local") {
@ -97,14 +98,18 @@ class HassioAddonStore extends LitElement {
.tabs=${supervisorTabs}
>
<span slot="header">Add-on store</span>
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
<ha-button-menu
corner="BOTTOM_START"
slot="toolbar-icon"
@action=${this._handleAction}
>
<mwc-icon-button slot="trigger" alt="menu">
<ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item @request-selected=${this._manageRepositories}>
<mwc-list-item>
Repositories
</mwc-list-item>
<mwc-list-item @request-selected=${this.refreshData}>
<mwc-list-item>
Reload
</mwc-list-item>
</ha-button-menu>
@ -143,6 +148,17 @@ class HassioAddonStore extends LitElement {
this._loadData();
}
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._manageRepositories();
break;
case 1:
this.refreshData();
break;
}
}
private apiCalled(ev) {
if (ev.detail.success) {
this._loadData();

View File

@ -0,0 +1,14 @@
import {
RequestSelectedDetail,
ListItem,
} from "@material/mwc-list/mwc-list-item";
export const shouldHandleRequestSelectedEvent = (
ev: CustomEvent<RequestSelectedDetail>
): boolean => {
if (!ev.detail.selected && ev.detail.source !== "property") {
return false;
}
(ev.target as ListItem).selected = false;
return true;
};

View File

@ -18,14 +18,30 @@ import "./ha-icon-button";
export class HaButtonMenu extends LitElement {
@property() public corner: Corner = "TOP_START";
@property({ type: Boolean }) public multi = false;
@property({ type: Boolean }) public activatable = false;
@query("mwc-menu") private _menu?: Menu;
public get items() {
return this._menu?.items;
}
public get selected() {
return this._menu?.selected;
}
protected render(): TemplateResult {
return html`
<div @click=${this._handleClick}>
<slot name="trigger"></slot>
</div>
<mwc-menu .corner=${this.corner}>
<mwc-menu
.corner=${this.corner}
.multi=${this.multi}
.activatable=${this.activatable}
>
<slot></slot>
</mwc-menu>
`;

View File

@ -18,6 +18,7 @@ import "@polymer/paper-input/paper-input";
import "@material/mwc-list/mwc-list";
import "./date-range-picker";
import { computeRTLDirection } from "../common/util/compute_rtl";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
export interface DateRangePickerRanges {
[key: string]: [Date, Date];
@ -85,15 +86,9 @@ export class HaDateRangePicker extends LitElement {
class="date-range-ranges"
.dir=${this._rtlDirection}
>
<mwc-list @request-selected=${this._setDateRange}>
${Object.entries(this.ranges).map(
([name, dates]) => html`<mwc-list-item
.activated=${this.startDate.getTime() ===
dates[0].getTime() &&
this.endDate.getTime() === dates[1].getTime()}
.startDate=${dates[0]}
.endDate=${dates[1]}
>
<mwc-list @action=${this._setDateRange} activatable>
${Object.keys(this.ranges).map(
(name) => html`<mwc-list-item>
${name}
</mwc-list-item>`
)}
@ -124,12 +119,10 @@ export class HaDateRangePicker extends LitElement {
);
}
private _setDateRange(ev: Event) {
const target = ev.target as any;
const startDate = target.startDate;
const endDate = target.endDate;
private _setDateRange(ev: CustomEvent<ActionDetail>) {
const dateRange = Object.values(this.ranges!)[ev.detail.index];
const dateRangePicker = this._dateRangePicker;
dateRangePicker.clickRange([startDate, endDate]);
dateRangePicker.clickRange(dateRange);
dateRangePicker.clickedApply();
}

View File

@ -29,8 +29,9 @@ import "./types/ha-automation-action-event";
import "./types/ha-automation-action-scene";
import "./types/ha-automation-action-service";
import "./types/ha-automation-action-wait_template";
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
import { handleStructError } from "../../../lovelace/common/structs/handle-errors";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import { haStyle } from "../../../../resources/styles";
const OPTIONS = [
"condition",
@ -134,17 +135,14 @@ export default class HaAutomationActionRow extends LitElement {
</mwc-icon-button>
`
: ""}
<ha-button-menu corner="BOTTOM_START">
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
<mwc-icon-button
slot="trigger"
.title=${this.hass.localize("ui.common.menu")}
.label=${this.hass.localize("ui.common.overflow_menu")}
><ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item
@request-selected=${this._switchYamlMode}
.disabled=${!this._uiModeAvailable}
>
<mwc-list-item .disabled=${!this._uiModeAvailable}>
${yamlMode
? this.hass.localize(
"ui.panel.config.automation.editor.edit_ui"
@ -158,7 +156,7 @@ export default class HaAutomationActionRow extends LitElement {
"ui.panel.config.automation.editor.actions.duplicate"
)}
</mwc-list-item>
<mwc-list-item @request-selected=${this._onDelete}>
<mwc-list-item>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.delete"
)}
@ -177,21 +175,20 @@ export default class HaAutomationActionRow extends LitElement {
: ""}
${yamlMode
? html`
<div style="margin-right: 24px;">
${selected === -1
? html`
${this.hass.localize(
"ui.panel.config.automation.editor.actions.unsupported_action",
"action",
type
)}
`
: ""}
<ha-yaml-editor
.defaultValue=${this.action}
@value-changed=${this._onYamlChange}
></ha-yaml-editor>
</div>
${selected === -1
? html`
${this.hass.localize(
"ui.panel.config.automation.editor.actions.unsupported_action",
"action",
type
)}
`
: ""}
<h2>Edit in YAML</h2>
<ha-yaml-editor
.defaultValue=${this.action}
@value-changed=${this._onYamlChange}
></ha-yaml-editor>
`
: html`
<paper-dropdown-menu-light
@ -243,6 +240,19 @@ export default class HaAutomationActionRow extends LitElement {
fireEvent(this, "move-action", { direction: "down" });
}
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._switchYamlMode();
break;
case 1:
break;
case 2:
this._onDelete();
break;
}
}
private _onDelete() {
showConfirmationDialog(this, {
text: this.hass.localize(
@ -288,40 +298,34 @@ export default class HaAutomationActionRow extends LitElement {
fireEvent(this, "value-changed", { value: ev.detail.value });
}
private _switchYamlMode(ev: CustomEvent<RequestSelectedDetail>) {
if (ev.detail.source !== "interaction") {
return;
}
private _switchYamlMode() {
this._yamlMode = !this._yamlMode;
}
static get styles(): CSSResult {
return css`
.card-menu {
position: absolute;
top: 0;
right: 0;
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
.rtl .card-menu {
right: auto;
left: 0;
}
ha-button-menu {
margin: 8px;
}
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
.warning {
color: var(--warning-color);
margin-bottom: 8px;
}
.warning ul {
margin: 4px 0;
}
`;
static get styles(): CSSResult[] {
return [
haStyle,
css`
.card-menu {
float: right;
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
.rtl .card-menu {
float: left;
}
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
.warning {
color: var(--warning-color);
margin-bottom: 8px;
}
.warning ul {
margin: 4px 0;
}
`,
];
}
}

View File

@ -2,7 +2,13 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import type { PaperListboxElement } from "@polymer/paper-listbox/paper-listbox";
import { customElement, html, LitElement, property } from "lit-element";
import {
customElement,
html,
LitElement,
property,
CSSResult,
} from "lit-element";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-card";
@ -19,6 +25,7 @@ import "./types/ha-automation-condition-sun";
import "./types/ha-automation-condition-template";
import "./types/ha-automation-condition-time";
import "./types/ha-automation-condition-zone";
import { haStyle } from "../../../../resources/styles";
const OPTIONS = [
"device",
@ -47,21 +54,20 @@ export default class HaAutomationConditionEditor extends LitElement {
return html`
${yamlMode
? html`
<div style="margin-right: 24px;">
${selected === -1
? html`
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.unsupported_condition",
"condition",
this.condition.condition
)}
`
: ""}
<ha-yaml-editor
.defaultValue=${this.condition}
@value-changed=${this._onYamlChange}
></ha-yaml-editor>
</div>
${selected === -1
? html`
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.unsupported_condition",
"condition",
this.condition.condition
)}
`
: ""}
<h2>Edit in YAML</h2>
<ha-yaml-editor
.defaultValue=${this.condition}
@value-changed=${this._onYamlChange}
></ha-yaml-editor>
`
: html`
<paper-dropdown-menu-light
@ -123,6 +129,10 @@ export default class HaAutomationConditionEditor extends LitElement {
}
fireEvent(this, "value-changed", { value: ev.detail.value });
}
static get styles(): CSSResult {
return haStyle;
}
}
declare global {

View File

@ -18,7 +18,7 @@ import { Condition } from "../../../../data/automation";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
import { HomeAssistant } from "../../../../types";
import "./ha-automation-condition-editor";
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
export interface ConditionElement extends LitElement {
condition: Condition;
@ -65,14 +65,14 @@ export default class HaAutomationConditionRow extends LitElement {
<ha-card>
<div class="card-content">
<div class="card-menu">
<ha-button-menu corner="BOTTOM_START">
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
<mwc-icon-button
.title=${this.hass.localize("ui.common.menu")}
.label=${this.hass.localize("ui.common.overflow_menu")}
slot="trigger"
><ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon
></mwc-icon-button>
<mwc-list-item @request-selected=${this._switchYamlMode}>
<mwc-list-item>
${this._yamlMode
? this.hass.localize(
"ui.panel.config.automation.editor.edit_ui"
@ -86,7 +86,7 @@ export default class HaAutomationConditionRow extends LitElement {
"ui.panel.config.automation.editor.actions.duplicate"
)}
</mwc-list-item>
<mwc-list-item @request-selected=${this._onDelete}>
<mwc-list-item>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.delete"
)}
@ -103,6 +103,19 @@ export default class HaAutomationConditionRow extends LitElement {
`;
}
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._switchYamlMode();
break;
case 1:
break;
case 2:
this._onDelete();
break;
}
}
private _onDelete() {
showConfirmationDialog(this, {
text: this.hass.localize(
@ -116,28 +129,19 @@ export default class HaAutomationConditionRow extends LitElement {
});
}
private _switchYamlMode(ev: CustomEvent<RequestSelectedDetail>) {
if (ev.detail.source !== "interaction") {
return;
}
private _switchYamlMode() {
this._yamlMode = !this._yamlMode;
}
static get styles(): CSSResult {
return css`
.card-menu {
position: absolute;
top: 0;
right: 0;
float: right;
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
.rtl .card-menu {
right: auto;
left: 0;
}
ha-button-menu {
margin: 8px;
float: left;
}
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);

View File

@ -34,7 +34,8 @@ import "./types/ha-automation-trigger-time";
import "./types/ha-automation-trigger-time_pattern";
import "./types/ha-automation-trigger-webhook";
import "./types/ha-automation-trigger-zone";
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import { haStyle } from "../../../../resources/styles";
const OPTIONS = [
"device",
@ -94,17 +95,14 @@ export default class HaAutomationTriggerRow extends LitElement {
<ha-card>
<div class="card-content">
<div class="card-menu">
<ha-button-menu corner="BOTTOM_START">
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
<mwc-icon-button
slot="trigger"
.title=${this.hass.localize("ui.common.menu")}
.label=${this.hass.localize("ui.common.overflow_menu")}
><ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon
></mwc-icon-button>
<mwc-list-item
@request-selected=${this._switchYamlMode}
.disabled=${selected === -1}
>
<mwc-list-item .disabled=${selected === -1}>
${yamlMode
? this.hass.localize(
"ui.panel.config.automation.editor.edit_ui"
@ -118,7 +116,7 @@ export default class HaAutomationTriggerRow extends LitElement {
"ui.panel.config.automation.editor.actions.duplicate"
)}
</mwc-list-item>
<mwc-list-item @request-selected=${this._onDelete}>
<mwc-list-item>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.delete"
)}
@ -127,21 +125,20 @@ export default class HaAutomationTriggerRow extends LitElement {
</div>
${yamlMode
? html`
<div style="margin-right: 24px;">
${selected === -1
? html`
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.unsupported_platform",
"platform",
this.trigger.platform
)}
`
: ""}
<ha-yaml-editor
.defaultValue=${this.trigger}
@value-changed=${this._onYamlChange}
></ha-yaml-editor>
</div>
${selected === -1
? html`
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.unsupported_platform",
"platform",
this.trigger.platform
)}
`
: ""}
<h2>Edit in YAML</h2>
<ha-yaml-editor
.defaultValue=${this.trigger}
@value-changed=${this._onYamlChange}
></ha-yaml-editor>
`
: html`
<paper-dropdown-menu-light
@ -178,6 +175,19 @@ export default class HaAutomationTriggerRow extends LitElement {
`;
}
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._switchYamlMode();
break;
case 1:
break;
case 2:
this._onDelete();
break;
}
}
private _onDelete() {
showConfirmationDialog(this, {
text: this.hass.localize(
@ -219,33 +229,27 @@ export default class HaAutomationTriggerRow extends LitElement {
fireEvent(this, "value-changed", { value: ev.detail.value });
}
private _switchYamlMode(ev: CustomEvent<RequestSelectedDetail>) {
if (ev.detail.source !== "interaction") {
return;
}
private _switchYamlMode() {
this._yamlMode = !this._yamlMode;
}
static get styles(): CSSResult {
return css`
.card-menu {
position: absolute;
top: 0;
right: 0;
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
.rtl .card-menu {
right: auto;
left: 0;
}
ha-button-menu {
margin: 8px;
}
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
`;
static get styles(): CSSResult[] {
return [
haStyle,
css`
.card-menu {
float: right;
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
.rtl .card-menu {
float: left;
}
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
`,
];
}
}

View File

@ -57,6 +57,7 @@ import {
showEntityEditorDialog,
} from "./show-dialog-entity-editor";
import { mdiFilterVariant } from "@mdi/js";
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
export interface StateEntity extends EntityRegistryEntry {
readonly?: boolean;
@ -449,7 +450,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
>
</div>`
: ""}
<ha-button-menu corner="BOTTOM_START">
<ha-button-menu corner="BOTTOM_START" multi>
<mwc-icon-button
slot="trigger"
.label=${this.hass!.localize(
@ -464,6 +465,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
<mwc-list-item
@request-selected="${this._showDisabledChanged}"
graphic="control"
.selected=${this._showDisabled}
>
<ha-checkbox
slot="graphic"
@ -476,6 +478,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
<mwc-list-item
@request-selected="${this._showRestoredChanged}"
graphic="control"
.selected=${this._showUnavailable}
>
<ha-checkbox
slot="graphic"
@ -488,6 +491,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
<mwc-list-item
@request-selected="${this._showReadOnlyChanged}"
graphic="control"
.selected=${this._showReadOnly}
>
<ha-checkbox
slot="graphic"
@ -579,16 +583,25 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
}
}
private _showDisabledChanged() {
this._showDisabled = !this._showDisabled;
private _showDisabledChanged(ev: CustomEvent<RequestSelectedDetail>) {
if (ev.detail.source !== "property") {
return;
}
this._showDisabled = ev.detail.selected;
}
private _showRestoredChanged() {
this._showUnavailable = !this._showUnavailable;
private _showRestoredChanged(ev: CustomEvent<RequestSelectedDetail>) {
if (ev.detail.source !== "property") {
return;
}
this._showUnavailable = ev.detail.selected;
}
private _showReadOnlyChanged() {
this._showReadOnly = !this._showReadOnly;
private _showReadOnlyChanged(ev: CustomEvent<RequestSelectedDetail>) {
if (ev.detail.source !== "property") {
return;
}
this._showReadOnly = ev.detail.selected;
}
private _handleSearchChange(ev: CustomEvent) {

View File

@ -276,7 +276,11 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
</div>
`
: ""}
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
<ha-button-menu
corner="BOTTOM_START"
slot="toolbar-icon"
@action=${this._toggleShowIgnored}
>
<mwc-icon-button
.title=${this.hass.localize("ui.common.menu")}
.label=${this.hass.localize("ui.common.overflow_menu")}
@ -284,7 +288,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
>
<ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item @request-selected=${this._toggleShowIgnored}>
<mwc-list-item>
${this.hass.localize(
this._showIgnored
? "ui.panel.config.integrations.ignore.hide_ignored"

View File

@ -28,6 +28,7 @@ import { haStyle } from "../../../resources/styles";
import "../../../components/ha-icon-next";
import { fireEvent } from "../../../common/dom/fire_event";
import { mdiDotsVertical, mdiOpenInNew } from "@mdi/js";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
export interface ConfigEntryUpdatedEvent {
entry: ConfigEntry;
@ -223,7 +224,7 @@ export class HaIntegrationCard extends LitElement {
`
: ""}
</div>
<ha-button-menu corner="BOTTOM_START">
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
<mwc-icon-button
.title=${this.hass.localize("ui.common.menu")}
.label=${this.hass.localize("ui.common.overflow_menu")}
@ -231,7 +232,7 @@ export class HaIntegrationCard extends LitElement {
>
<ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item @request-selected=${this._showSystemOptions}>
<mwc-list-item>
${this.hass.localize(
"ui.panel.config.integrations.config_entry.system_options"
)}
@ -240,7 +241,6 @@ export class HaIntegrationCard extends LitElement {
? ""
: html`
<a
class="documentation"
href=${this.manifest.documentation}
rel="noreferrer"
target="_blank"
@ -255,10 +255,7 @@ export class HaIntegrationCard extends LitElement {
</mwc-list-item>
</a>
`}
<mwc-list-item
class="warning"
@request-selected=${this._removeIntegration}
>
<mwc-list-item class="warning">
${this.hass.localize(
"ui.panel.config.integrations.config_entry.delete"
)}
@ -308,32 +305,27 @@ export class HaIntegrationCard extends LitElement {
showOptionsFlowDialog(this, ev.target.closest("ha-card").configEntry);
}
private _showSystemOptions(ev) {
showConfigEntrySystemOptionsDialog(this, {
entry: ev.target.closest("ha-card").configEntry,
});
}
private async _editEntryName(ev) {
const configEntry = ev.target.closest("ha-card").configEntry;
const newName = await showPromptDialog(this, {
title: this.hass.localize("ui.panel.config.integrations.rename_dialog"),
defaultValue: configEntry.title,
inputLabel: this.hass.localize(
"ui.panel.config.integrations.rename_input_label"
),
});
if (newName === null) {
return;
private _handleAction(ev: CustomEvent<ActionDetail>) {
const configEntry = ((ev.target as HTMLElement).closest("ha-card") as any)
.configEntry;
switch (ev.detail.index) {
case 0:
this._showSystemOptions(configEntry);
break;
case 1:
this._removeIntegration(configEntry);
break;
}
const newEntry = await updateConfigEntry(this.hass, configEntry.entry_id, {
title: newName,
});
fireEvent(this, "entry-updated", { entry: newEntry });
}
private async _removeIntegration(ev) {
const entryId = ev.target.closest("ha-card").configEntry.entry_id;
private _showSystemOptions(configEntry: ConfigEntry) {
showConfigEntrySystemOptionsDialog(this, {
entry: configEntry,
});
}
private async _removeIntegration(configEntry: ConfigEntry) {
const entryId = configEntry.entry_id;
const confirmed = await showConfirmationDialog(this, {
text: this.hass.localize(
@ -357,6 +349,24 @@ export class HaIntegrationCard extends LitElement {
});
}
private async _editEntryName(ev) {
const configEntry = ev.target.closest("ha-card").configEntry;
const newName = await showPromptDialog(this, {
title: this.hass.localize("ui.panel.config.integrations.rename_dialog"),
defaultValue: configEntry.title,
inputLabel: this.hass.localize(
"ui.panel.config.integrations.rename_input_label"
),
});
if (newName === null) {
return;
}
const newEntry = await updateConfigEntry(this.hass, configEntry.entry_id, {
title: newName,
});
fireEvent(this, "entry-updated", { entry: newEntry });
}
static get styles(): CSSResult[] {
return [
haStyle,
@ -389,9 +399,6 @@ export class HaIntegrationCard extends LitElement {
align-items: center;
padding-right: 5px;
}
.card-actions .documentation {
color: var(--primary-text-color);
}
.group-header {
display: flex;
align-items: center;

View File

@ -20,6 +20,7 @@ import { confDeleteCard } from "../editor/delete-card";
import { Lovelace, LovelaceCard } from "../types";
import { computeCardSize } from "../common/compute-card-size";
import { mdiDotsVertical, mdiArrowDown, mdiArrowUp } from "@mdi/js";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
@customElement("hui-card-options")
export class HuiCardOptions extends LitElement {
@ -65,7 +66,7 @@ export class HuiCardOptions extends LitElement {
?disabled=${this.path![1] === 0}
><ha-svg-icon path=${mdiArrowUp}></ha-svg-icon
></mwc-icon-button>
<ha-button-menu corner="BOTTOM_START">
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
<mwc-icon-button
slot="trigger"
aria-label=${this.hass!.localize(
@ -78,20 +79,17 @@ export class HuiCardOptions extends LitElement {
<ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item @request-selected=${this._moveCard}>
<mwc-list-item>
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.move"
)}</mwc-list-item
>
<mwc-list-item @request-selected=${this._duplicateCard}
<mwc-list-item
>${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.duplicate"
)}</mwc-list-item
>
<mwc-list-item
class="delete-item"
@request-selected=${this._deleteCard}
>
<mwc-list-item class="delete-item">
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.delete"
)}</mwc-list-item
@ -150,6 +148,20 @@ export class HuiCardOptions extends LitElement {
`;
}
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._moveCard();
break;
case 1:
this._duplicateCard();
break;
case 2:
this._deleteCard();
break;
}
}
private _duplicateCard(): void {
const path = this.path!;
const cardConfig = this.lovelace!.config.views[path[0]].cards![path[1]];

View File

@ -58,6 +58,8 @@ import type { Lovelace } from "./types";
import "./views/hui-panel-view";
import type { HUIPanelView } from "./views/hui-panel-view";
import { HUIView } from "./views/hui-view";
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
class HUIRoot extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@ -133,14 +135,19 @@ class HUIRoot extends LitElement {
<ha-svg-icon path=${mdiPencil}></ha-svg-icon>
</mwc-icon-button>
</div>
<mwc-icon-button
title="${this.hass!.localize(
"ui.panel.lovelace.menu.help"
)}"
@click="${this._handleHelp}"
<a
href="https://www.home-assistant.io/lovelace/"
rel="noreferrer"
target="_blank"
>
<ha-svg-icon path=${mdiHelpCircle}></ha-svg-icon>
</mwc-icon-button>
<mwc-icon-button
title="${this.hass!.localize(
"ui.panel.lovelace.menu.help"
)}"
>
<ha-svg-icon path=${mdiHelpCircle}></ha-svg-icon>
</mwc-icon-button>
</a>
<ha-button-menu corner="BOTTOM_START">
<mwc-icon-button
slot="trigger"
@ -167,9 +174,7 @@ class HUIRoot extends LitElement {
)}
</mwc-list-item>
`}
<mwc-list-item
@request-selected="${this.lovelace!.enableFullEditMode}"
>
<mwc-list-item @request-selected="${this._handleRawEditor}">
${this.hass!.localize(
"ui.panel.lovelace.editor.menu.raw_editor"
)}
@ -251,7 +256,7 @@ class HUIRoot extends LitElement {
aria-label=${this.hass!.localize(
"ui.panel.lovelace.menu.configure_ui"
)}
@request-selected=${this._editModeEnable}
@request-selected=${this._handleEnableEditMode}
>
${this.hass!.localize(
"ui.panel.lovelace.menu.configure_ui"
@ -259,14 +264,19 @@ class HUIRoot extends LitElement {
</mwc-list-item>
`
: ""}
<mwc-list-item
aria-label=${this.hass!.localize(
"ui.panel.lovelace.menu.help"
)}
@request-selected=${this._handleHelp}
<a
href="https://www.home-assistant.io/lovelace/"
rel="noreferrer"
target="_blank"
>
${this.hass!.localize("ui.panel.lovelace.menu.help")}
</mwc-list-item>
<mwc-list-item
aria-label=${this.hass!.localize(
"ui.panel.lovelace.menu.help"
)}
>
${this.hass!.localize("ui.panel.lovelace.menu.help")}
</mwc-list-item>
</a>
</ha-button-menu>
</app-toolbar>
`}
@ -476,11 +486,17 @@ class HUIRoot extends LitElement {
return this.shadowRoot!.getElementById("view") as HTMLDivElement;
}
private _handleRefresh(): void {
private _handleRefresh(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
fireEvent(this, "config-refresh");
}
private _handleReloadResources(): void {
private _handleReloadResources(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
this.hass.callService("lovelace", "reload_resources");
showConfirmationDialog(this, {
title: this.hass!.localize(
@ -493,7 +509,17 @@ class HUIRoot extends LitElement {
});
}
private _handleUnusedEntities(): void {
private _handleRawEditor(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
this.lovelace!.enableFullEditMode();
}
private _handleUnusedEntities(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
navigate(this, `${this.route?.prefix}/hass-unused-entities`);
}
@ -501,17 +527,20 @@ class HUIRoot extends LitElement {
showVoiceCommandDialog(this);
}
private _handleHelp(): void {
window.open("https://www.home-assistant.io/lovelace/", "_blank");
}
private _editModeEnable(): void {
private _handleEnableEditMode(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
if (this._yamlMode) {
showAlertDialog(this, {
text: "The edit UI is not available when in YAML mode.",
});
return;
}
this._enableEditMode();
}
private _enableEditMode(): void {
this.lovelace!.setEditMode(true);
}
@ -616,7 +645,7 @@ class HUIRoot extends LitElement {
const viewConfig = this.config.views[viewIndex];
if (!viewConfig) {
this._editModeEnable();
this._enableEditMode();
return;
}

View File

@ -83,14 +83,14 @@ class HaPanelShoppingList extends LocalizeMixin(PolymerElement) {
icon="hass:microphone"
on-click="_showVoiceCommandDialog"
></ha-icon-button>
<ha-button-menu corner="BOTTOM_START">
<ha-button-menu corner="BOTTOM_START" on-action="_clearCompleted">
<ha-icon-button
icon="hass:dots-vertical"
label="Menu"
slot="trigger"
>
</ha-icon-button>
<mwc-list-item on-request-selected="_clearCompleted">
<mwc-list-item>
[[localize('ui.panel.shopping-list.clear_completed')]]
</mwc-list-item>
</ha-button-menu>