From ec257710ffc4298ba9a2f270b39b9900ee0a1d31 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 2 Sep 2022 19:01:05 +0200 Subject: [PATCH] Add overflow menu to automation picker (#13569) --- src/components/ha-icon-overflow-menu.ts | 27 ++- .../config/automation/ha-automation-picker.ts | 187 ++++++++++++++++-- src/translations/en.json | 2 + 3 files changed, 188 insertions(+), 28 deletions(-) diff --git a/src/components/ha-icon-overflow-menu.ts b/src/components/ha-icon-overflow-menu.ts index 4f81550e30..6ff579c2a8 100644 --- a/src/components/ha-icon-overflow-menu.ts +++ b/src/components/ha-icon-overflow-menu.ts @@ -3,6 +3,8 @@ import { mdiDotsVertical } from "@mdi/js"; import "@polymer/paper-tooltip/paper-tooltip"; import { css, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { haStyle } from "../resources/styles"; import { HomeAssistant } from "../types"; import "./ha-button-menu"; import "./ha-icon-button"; @@ -16,6 +18,7 @@ export interface IconOverflowMenuItem { disabled?: boolean; tooltip?: string; onClick: CallableFunction; + warning?: boolean; } @customElement("ha-icon-overflow-menu") @@ -49,9 +52,13 @@ export class HaIconOverflowMenu extends LitElement { graphic="icon" .disabled=${item.disabled} @click=${item.action} + class=${classMap({ warning: Boolean(item.warning) })} >
- +
${item.label} @@ -81,7 +88,8 @@ export class HaIconOverflowMenu extends LitElement { `; } - protected _handleIconOverflowMenuOpened() { + protected _handleIconOverflowMenuOpened(e) { + e.stopPropagation(); // If this component is used inside a data table, the z-index of the row // needs to be increased. Otherwise the ha-button-menu would be displayed // underneath the next row in the table. @@ -99,12 +107,15 @@ export class HaIconOverflowMenu extends LitElement { } static get styles() { - return css` - :host { - display: flex; - justify-content: flex-end; - } - `; + return [ + haStyle, + css` + :host { + display: flex; + justify-content: flex-end; + } + `, + ]; } } diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index aba85d255b..7ee6e86dda 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -1,4 +1,16 @@ -import { mdiHelpCircle, mdiInformationOutline, mdiPlus } from "@mdi/js"; +import { + mdiCancel, + mdiContentDuplicate, + mdiDelete, + mdiHelpCircle, + mdiInformationOutline, + mdiPlay, + mdiPlayCircleOutline, + mdiPlus, + mdiStopCircleOutline, + mdiTransitConnection, +} from "@mdi/js"; +import "@polymer/paper-tooltip/paper-tooltip"; import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; @@ -13,11 +25,22 @@ import type { RowClickedEvent, } from "../../../components/data-table/ha-data-table"; import "../../../components/ha-button-related-filter-menu"; +import "../../../components/ha-chip"; import "../../../components/ha-fab"; import "../../../components/ha-icon-button"; +import "../../../components/ha-icon-overflow-menu"; import "../../../components/ha-svg-icon"; -import type { AutomationEntity } from "../../../data/automation"; -import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; +import { + AutomationEntity, + deleteAutomation, + getAutomationConfig, + showAutomationEditor, + triggerAutomationActions, +} from "../../../data/automation"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../dialogs/generic/show-dialog-box"; import "../../../layouts/hass-tabs-subpage-data-table"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; @@ -63,6 +86,7 @@ class HaAutomationPicker extends LitElement { ...automation, name: computeStateName(automation), last_triggered: automation.attributes.last_triggered || undefined, + disabled: automation.state === "off", })); } ); @@ -123,22 +147,105 @@ class HaAutomationPicker extends LitElement { }, }; } + + columns.disabled = this.narrow + ? { + title: "", + template: (disabled: boolean) => + disabled + ? html` + + ${this.hass.localize( + "ui.panel.config.automation.picker.disabled" + )} + + + ` + : "", + } + : { + width: "20%", + title: "", + template: (disabled: boolean) => + disabled + ? html` + + ${this.hass.localize( + "ui.panel.config.automation.picker.disabled" + )} + + ` + : "", + }; + columns.actions = { title: "", - label: this.hass.localize( - "ui.panel.config.automation.picker.headers.actions" - ), - type: "icon-button", - template: (_info, automation: any) => html` - - `, + width: this.narrow ? undefined : "10%", + type: "overflow-menu", + template: (_: string, automation: any) => + html` + this._showInfo(automation), + }, + { + path: mdiPlay, + label: this.hass.localize( + "ui.panel.config.automation.editor.run" + ), + action: () => this._runActions(automation), + }, + { + path: mdiTransitConnection, + label: this.hass.localize( + "ui.panel.config.automation.editor.show_trace" + ), + action: () => this._showTrace(automation), + }, + { + path: mdiContentDuplicate, + label: this.hass.localize( + "ui.panel.config.automation.picker.duplicate" + ), + action: () => this.duplicate(automation), + }, + { + path: + automation.state === "off" + ? mdiPlayCircleOutline + : mdiStopCircleOutline, + label: + automation.state === "off" + ? this.hass.localize( + "ui.panel.config.automation.editor.enable" + ) + : this.hass.localize( + "ui.panel.config.automation.editor.disable" + ), + action: () => this._toggle(automation), + }, + { + label: this.hass.localize( + "ui.panel.config.automation.picker.delete" + ), + path: mdiDelete, + action: () => this._deleteConfirm(automation), + warning: true, + }, + ]} + > + + `, }; return columns; } @@ -210,12 +317,52 @@ class HaAutomationPicker extends LitElement { this._filterValue = undefined; } - private _showInfo(ev) { - ev.stopPropagation(); - const automation = ev.currentTarget.automation; + private _showInfo(automation: any) { fireEvent(this, "hass-more-info", { entityId: automation.entity_id }); } + private _runActions(automation: any) { + triggerAutomationActions(this.hass, automation.entity_id); + } + + private _showTrace(automation: any) { + navigate(`/config/automation/trace/${automation.attributes.id}`); + } + + private async _toggle(automation): Promise { + const service = automation.state === "off" ? "turn_on" : "turn_off"; + await this.hass.callService("automation", service, { + entity_id: automation.entity_id, + }); + } + + private async _deleteConfirm(automation) { + showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.config.automation.picker.delete_confirm" + ), + confirmText: this.hass!.localize("ui.common.delete"), + dismissText: this.hass!.localize("ui.common.cancel"), + confirm: () => this._delete(automation), + }); + } + + private async _delete(automation) { + await deleteAutomation(this.hass, automation.attributes.id); + } + + private async duplicate(automation) { + const config = await getAutomationConfig( + this.hass, + automation.attributes.id + ); + showAutomationEditor({ + ...config, + id: undefined, + alias: undefined, + }); + } + private _showHelp() { showAlertDialog(this, { title: this.hass.localize("ui.panel.config.automation.caption"), diff --git a/src/translations/en.json b/src/translations/en.json index 920ec90499..e38adf3c96 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1793,6 +1793,7 @@ "delete": "Delete", "delete_confirm": "Are you sure you want to delete this automation?", "duplicate": "Duplicate", + "disabled": "Disabled", "headers": { "toggle": "Enable/disable", "name": "Name", @@ -1822,6 +1823,7 @@ "run": "[%key:ui::panel::config::automation::editor::actions::run%]", "rename": "[%key:ui::panel::config::automation::editor::triggers::rename%]", "show_trace": "Traces", + "show_info": "Information", "introduction": "Use automations to bring your home to life.", "default_name": "New Automation", "missing_name": "Cannot save automation without a name",