mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Add cut / copy / paste support to automation & script editor (#16488)
This commit is contained in:
parent
31c89d70c5
commit
29846a168e
@ -379,3 +379,9 @@ export const testCondition = (
|
||||
condition,
|
||||
variables,
|
||||
});
|
||||
|
||||
export type Clipboard = {
|
||||
trigger?: Trigger;
|
||||
condition?: Condition;
|
||||
action?: Action;
|
||||
};
|
||||
|
@ -3,6 +3,8 @@ import "@material/mwc-list/mwc-list-item";
|
||||
import {
|
||||
mdiCheck,
|
||||
mdiContentDuplicate,
|
||||
mdiContentCopy,
|
||||
mdiContentCut,
|
||||
mdiDelete,
|
||||
mdiDotsVertical,
|
||||
mdiPlay,
|
||||
@ -31,6 +33,7 @@ import {
|
||||
EntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../../data/entity_registry";
|
||||
import { Clipboard } from "../../../../data/automation";
|
||||
import { Action, getActionType } from "../../../../data/script";
|
||||
import { describeAction } from "../../../../data/script_i18n";
|
||||
import { callExecuteScript } from "../../../../data/service";
|
||||
@ -57,7 +60,7 @@ import "./types/ha-automation-action-stop";
|
||||
import "./types/ha-automation-action-wait_for_trigger";
|
||||
import "./types/ha-automation-action-wait_template";
|
||||
|
||||
const getType = (action: Action | undefined) => {
|
||||
export const getType = (action: Action | undefined) => {
|
||||
if (!action) {
|
||||
return undefined;
|
||||
}
|
||||
@ -112,6 +115,8 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
@state() private _entityReg: EntityRegistryEntry[] = [];
|
||||
|
||||
@state() private _warnings?: string[];
|
||||
@ -214,6 +219,9 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
)}
|
||||
<ha-svg-icon slot="graphic" .path=${mdiSort}></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<li divider role="separator"></li>
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
@ -224,6 +232,26 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.copy"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiContentCopy}
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.cut"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiContentCut}
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<li divider role="separator"></li>
|
||||
|
||||
<mwc-list-item
|
||||
@ -344,6 +372,7 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
narrow: this.narrow,
|
||||
reOrderMode: this.reOrderMode,
|
||||
disabled: this.disabled,
|
||||
clipboard: this.clipboard,
|
||||
})}
|
||||
</div>
|
||||
`}
|
||||
@ -378,17 +407,24 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
fireEvent(this, "duplicate");
|
||||
break;
|
||||
case 4:
|
||||
fireEvent(this, "set-clipboard", { action: this.action });
|
||||
break;
|
||||
case 5:
|
||||
fireEvent(this, "set-clipboard", { action: this.action });
|
||||
fireEvent(this, "value-changed", { value: null });
|
||||
break;
|
||||
case 6:
|
||||
this._switchUiMode();
|
||||
this.expand();
|
||||
break;
|
||||
case 5:
|
||||
case 7:
|
||||
this._switchYamlMode();
|
||||
this.expand();
|
||||
break;
|
||||
case 6:
|
||||
case 8:
|
||||
this._onDisable();
|
||||
break;
|
||||
case 7:
|
||||
case 9:
|
||||
this._onDelete();
|
||||
break;
|
||||
}
|
||||
|
@ -1,8 +1,21 @@
|
||||
import "@material/mwc-button";
|
||||
import type { ActionDetail } from "@material/mwc-list";
|
||||
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
mdiArrowDown,
|
||||
mdiArrowUp,
|
||||
mdiDrag,
|
||||
mdiPlus,
|
||||
mdiContentPaste,
|
||||
} from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
PropertyValues,
|
||||
} from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
@ -16,13 +29,14 @@ import type { HaSelect } from "../../../../components/ha-select";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import { ACTION_TYPES } from "../../../../data/action";
|
||||
import { Action } from "../../../../data/script";
|
||||
import { Clipboard } from "../../../../data/automation";
|
||||
import { sortableStyles } from "../../../../resources/ha-sortable-style";
|
||||
import {
|
||||
loadSortable,
|
||||
SortableInstance,
|
||||
} from "../../../../resources/sortable.ondemand";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import "./ha-automation-action-row";
|
||||
import { getType } from "./ha-automation-action-row";
|
||||
import type HaAutomationActionRow from "./ha-automation-action-row";
|
||||
import "./types/ha-automation-action-activate_scene";
|
||||
import "./types/ha-automation-action-choose";
|
||||
@ -39,6 +53,8 @@ import "./types/ha-automation-action-stop";
|
||||
import "./types/ha-automation-action-wait_for_trigger";
|
||||
import "./types/ha-automation-action-wait_template";
|
||||
|
||||
const PASTE_VALUE = "__paste__";
|
||||
|
||||
@customElement("ha-automation-action")
|
||||
export default class HaAutomationAction extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -53,6 +69,8 @@ export default class HaAutomationAction extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
private _focusLastActionOnChange = false;
|
||||
|
||||
private _actionKeys = new WeakMap<Action, string>();
|
||||
@ -95,6 +113,7 @@ export default class HaAutomationAction extends LitElement {
|
||||
@duplicate=${this._duplicateAction}
|
||||
@value-changed=${this._actionChanged}
|
||||
@re-order=${this._enterReOrderMode}
|
||||
.clipboard=${this.clipboard}
|
||||
.hass=${this.hass}
|
||||
>
|
||||
${this.reOrderMode
|
||||
@ -139,6 +158,19 @@ export default class HaAutomationAction extends LitElement {
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
|
||||
</ha-button>
|
||||
${this.clipboard?.action
|
||||
? html` <mwc-list-item .value=${PASTE_VALUE} graphic="icon">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.paste"
|
||||
)}
|
||||
(${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.actions.type.${getType(
|
||||
this.clipboard.action
|
||||
)}.label`
|
||||
)})
|
||||
<ha-svg-icon slot="graphic" .path=${mdiContentPaste}></ha-svg-icon
|
||||
></mwc-list-item>`
|
||||
: nothing}
|
||||
${this._processedTypes(this.hass.localize).map(
|
||||
([opt, label, icon]) => html`
|
||||
<mwc-list-item .value=${opt} graphic="icon">
|
||||
@ -221,13 +253,19 @@ export default class HaAutomationAction extends LitElement {
|
||||
|
||||
private _addAction(ev: CustomEvent<ActionDetail>) {
|
||||
const action = (ev.currentTarget as HaSelect).items[ev.detail.index].value;
|
||||
const elClass = customElements.get(
|
||||
`ha-automation-action-${action}`
|
||||
) as CustomElementConstructor & { defaultConfig: Action };
|
||||
|
||||
const actions = this.actions.concat({
|
||||
...elClass.defaultConfig,
|
||||
});
|
||||
let actions: Action[];
|
||||
if (action === PASTE_VALUE) {
|
||||
actions = this.actions.concat(deepClone(this.clipboard!.action));
|
||||
} else {
|
||||
const elClass = customElements.get(
|
||||
`ha-automation-action-${action}`
|
||||
) as CustomElementConstructor & { defaultConfig: Action };
|
||||
|
||||
actions = this.actions.concat({
|
||||
...elClass.defaultConfig,
|
||||
});
|
||||
}
|
||||
this._focusLastActionOnChange = true;
|
||||
fireEvent(this, "value-changed", { value: actions });
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ensureArray } from "../../../../../common/array/ensure-array";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-button";
|
||||
import { Condition } from "../../../../../data/automation";
|
||||
import { Condition, Clipboard } from "../../../../../data/automation";
|
||||
import { Action, ChooseAction } from "../../../../../data/script";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
@ -23,6 +23,8 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
||||
|
||||
@state() private _showDefault = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
public static get defaultConfig() {
|
||||
return { choose: [{ conditions: [], sequence: [] }] };
|
||||
}
|
||||
@ -63,6 +65,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
||||
.hass=${this.hass}
|
||||
.idx=${idx}
|
||||
@value-changed=${this._conditionChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
></ha-automation-condition>
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
@ -77,6 +80,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
||||
.hass=${this.hass}
|
||||
.idx=${idx}
|
||||
@value-changed=${this._actionChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
></ha-automation-action>
|
||||
</div>
|
||||
</ha-card>`
|
||||
@ -105,6 +109,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._defaultChanged}
|
||||
.hass=${this.hass}
|
||||
.clipboard=${this.clipboard}
|
||||
></ha-automation-action>
|
||||
`
|
||||
: html`<div class="link-button-row">
|
||||
|
@ -6,7 +6,7 @@ import { stringCompare } from "../../../../../common/string/compare";
|
||||
import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
||||
import "../../../../../components/ha-select";
|
||||
import type { HaSelect } from "../../../../../components/ha-select";
|
||||
import type { Condition } from "../../../../../data/automation";
|
||||
import type { Condition, Clipboard } from "../../../../../data/automation";
|
||||
import { CONDITION_TYPES } from "../../../../../data/condition";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../condition/ha-automation-condition-editor";
|
||||
@ -20,6 +20,8 @@ export class HaConditionAction extends LitElement implements ActionElement {
|
||||
|
||||
@property() public action!: Condition;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
public static get defaultConfig() {
|
||||
return { condition: "state" };
|
||||
}
|
||||
@ -49,6 +51,7 @@ export class HaConditionAction extends LitElement implements ActionElement {
|
||||
.disabled=${this.disabled}
|
||||
.hass=${this.hass}
|
||||
@value-changed=${this._conditionChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
></ha-automation-condition-editor>
|
||||
`;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { Action, IfAction } from "../../../../../data/script";
|
||||
import type { Clipboard } from "../../../../../data/automation";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { Condition } from "../../../../lovelace/common/validate-condition";
|
||||
@ -19,6 +20,8 @@ export class HaIfAction extends LitElement implements ActionElement {
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
@state() private _showElse = false;
|
||||
|
||||
public static get defaultConfig() {
|
||||
@ -43,6 +46,7 @@ export class HaIfAction extends LitElement implements ActionElement {
|
||||
.reOrderMode=${this.reOrderMode}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._ifChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-condition>
|
||||
|
||||
@ -57,6 +61,7 @@ export class HaIfAction extends LitElement implements ActionElement {
|
||||
.reOrderMode=${this.reOrderMode}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._thenChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-action>
|
||||
${this._showElse || action.else
|
||||
|
@ -2,6 +2,7 @@ import { CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { Action, ParallelAction } from "../../../../../data/script";
|
||||
import type { Clipboard } from "../../../../../data/automation";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../ha-automation-action";
|
||||
@ -18,6 +19,8 @@ export class HaParallelAction extends LitElement implements ActionElement {
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
public static get defaultConfig() {
|
||||
return {
|
||||
parallel: [],
|
||||
@ -34,6 +37,7 @@ export class HaParallelAction extends LitElement implements ActionElement {
|
||||
.reOrderMode=${this.reOrderMode}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._actionsChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-action>
|
||||
`;
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
UntilRepeat,
|
||||
WhileRepeat,
|
||||
} from "../../../../../data/script";
|
||||
import type { Clipboard } from "../../../../../data/automation";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { Condition } from "../../../../lovelace/common/validate-condition";
|
||||
@ -29,6 +30,8 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
public static get defaultConfig() {
|
||||
return { repeat: { count: 2, sequence: [] } };
|
||||
}
|
||||
@ -82,6 +85,7 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._conditionChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
></ha-automation-condition>`
|
||||
: type === "until"
|
||||
? html` <h3>
|
||||
@ -95,6 +99,7 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._conditionChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
></ha-automation-condition>`
|
||||
: ""}
|
||||
</div>
|
||||
@ -109,6 +114,7 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
||||
.reOrderMode=${this.reOrderMode}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._actionChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
.hass=${this.hass}
|
||||
></ha-automation-action>
|
||||
`;
|
||||
|
@ -4,6 +4,7 @@ import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-formfield";
|
||||
import { WaitForTriggerAction } from "../../../../../data/script";
|
||||
import type { Clipboard } from "../../../../../data/automation";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../trigger/ha-automation-trigger";
|
||||
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
||||
@ -25,6 +26,8 @@ export class HaWaitForTriggerAction
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
public static get defaultConfig() {
|
||||
return { wait_for_trigger: [] };
|
||||
}
|
||||
@ -62,6 +65,7 @@ export class HaWaitForTriggerAction
|
||||
.name=${"wait_for_trigger"}
|
||||
.reOrderMode=${this.reOrderMode}
|
||||
@value-changed=${this._valueChanged}
|
||||
.clipboard=${this.clipboard}
|
||||
></ha-automation-trigger>
|
||||
`;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import memoizeOne from "memoize-one";
|
||||
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-yaml-editor";
|
||||
import type { Condition } from "../../../../data/automation";
|
||||
import type { Condition, Clipboard } from "../../../../data/automation";
|
||||
import { expandConditionWithShorthand } from "../../../../data/automation";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
@ -32,6 +32,8 @@ export default class HaAutomationConditionEditor extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
private _processedCondition = memoizeOne((condition) =>
|
||||
expandConditionWithShorthand(condition)
|
||||
);
|
||||
@ -70,6 +72,7 @@ export default class HaAutomationConditionEditor extends LitElement {
|
||||
condition: condition,
|
||||
reOrderMode: this.reOrderMode,
|
||||
disabled: this.disabled,
|
||||
clipboard: this.clipboard,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
|
@ -3,6 +3,8 @@ import "@material/mwc-list/mwc-list-item";
|
||||
import {
|
||||
mdiCheck,
|
||||
mdiContentDuplicate,
|
||||
mdiContentCopy,
|
||||
mdiContentCut,
|
||||
mdiDelete,
|
||||
mdiDotsVertical,
|
||||
mdiFlask,
|
||||
@ -22,6 +24,7 @@ import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import { Condition, testCondition } from "../../../../data/automation";
|
||||
import type { Clipboard } from "../../../../data/automation";
|
||||
import { describeCondition } from "../../../../data/automation_i18n";
|
||||
import { CONDITION_TYPES } from "../../../../data/condition";
|
||||
import { validateConfig } from "../../../../data/config";
|
||||
@ -77,6 +80,8 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
@state() private _yamlMode = false;
|
||||
|
||||
@state() private _warnings?: string[];
|
||||
@ -149,6 +154,8 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
<ha-svg-icon slot="graphic" .path=${mdiSort}></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<li divider role="separator"></li>
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
@ -159,6 +166,26 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.copy"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiContentCopy}
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.cut"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiContentCut}
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<li divider role="separator"></li>
|
||||
|
||||
<mwc-list-item graphic="icon">
|
||||
@ -255,6 +282,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.condition=${this.condition}
|
||||
.reOrderMode=${this.reOrderMode}
|
||||
.clipboard=${this.clipboard}
|
||||
></ha-automation-condition-editor>
|
||||
</div>
|
||||
</ha-expansion-panel>
|
||||
@ -307,17 +335,24 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
fireEvent(this, "duplicate");
|
||||
break;
|
||||
case 4:
|
||||
fireEvent(this, "set-clipboard", { condition: this.condition });
|
||||
break;
|
||||
case 5:
|
||||
fireEvent(this, "set-clipboard", { condition: this.condition });
|
||||
fireEvent(this, "value-changed", { value: null });
|
||||
break;
|
||||
case 6:
|
||||
this._switchUiMode();
|
||||
this.expand();
|
||||
break;
|
||||
case 5:
|
||||
case 7:
|
||||
this._switchYamlMode();
|
||||
this.expand();
|
||||
break;
|
||||
case 6:
|
||||
case 8:
|
||||
this._onDisable();
|
||||
break;
|
||||
case 7:
|
||||
case 9:
|
||||
this._onDelete();
|
||||
break;
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
import "@material/mwc-button";
|
||||
import type { ActionDetail } from "@material/mwc-list";
|
||||
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
mdiArrowDown,
|
||||
mdiArrowUp,
|
||||
mdiDrag,
|
||||
mdiPlus,
|
||||
mdiContentPaste,
|
||||
} from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import {
|
||||
css,
|
||||
@ -18,7 +24,7 @@ import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-button-menu";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import type { Condition } from "../../../../data/automation";
|
||||
import type { Condition, Clipboard } from "../../../../data/automation";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import "./ha-automation-condition-row";
|
||||
import type HaAutomationConditionRow from "./ha-automation-condition-row";
|
||||
@ -44,6 +50,8 @@ import "./types/ha-automation-condition-time";
|
||||
import "./types/ha-automation-condition-trigger";
|
||||
import "./types/ha-automation-condition-zone";
|
||||
|
||||
const PASTE_VALUE = "__paste__";
|
||||
|
||||
@customElement("ha-automation-condition")
|
||||
export default class HaAutomationCondition extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -56,6 +64,8 @@ export default class HaAutomationCondition extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
private _focusLastConditionOnChange = false;
|
||||
|
||||
private _conditionKeys = new WeakMap<Condition, string>();
|
||||
@ -147,6 +157,7 @@ export default class HaAutomationCondition extends LitElement {
|
||||
@move-condition=${this._move}
|
||||
@value-changed=${this._conditionChanged}
|
||||
@re-order=${this._enterReOrderMode}
|
||||
.clipboard=${this.clipboard}
|
||||
.hass=${this.hass}
|
||||
>
|
||||
${this.reOrderMode
|
||||
@ -191,6 +202,17 @@ export default class HaAutomationCondition extends LitElement {
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
|
||||
</ha-button>
|
||||
${this.clipboard?.condition
|
||||
? html` <mwc-list-item .value=${PASTE_VALUE} graphic="icon">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.paste"
|
||||
)}
|
||||
(${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.conditions.type.${this.clipboard.condition.condition}.label`
|
||||
)})
|
||||
<ha-svg-icon slot="graphic" .path=${mdiContentPaste}></ha-svg-icon
|
||||
></mwc-list-item>`
|
||||
: nothing}
|
||||
${this._processedTypes(this.hass.localize).map(
|
||||
([opt, label, icon]) => html`
|
||||
<mwc-list-item .value=${opt} graphic="icon">
|
||||
@ -251,19 +273,25 @@ export default class HaAutomationCondition extends LitElement {
|
||||
}
|
||||
|
||||
private _addCondition(ev: CustomEvent<ActionDetail>) {
|
||||
const condition = (ev.currentTarget as HaSelect).items[ev.detail.index]
|
||||
.value as Condition["condition"];
|
||||
const value = (ev.currentTarget as HaSelect).items[ev.detail.index].value;
|
||||
|
||||
const elClass = customElements.get(
|
||||
`ha-automation-condition-${condition}`
|
||||
) as CustomElementConstructor & {
|
||||
defaultConfig: Omit<Condition, "condition">;
|
||||
};
|
||||
let conditions: Condition[];
|
||||
if (value === PASTE_VALUE) {
|
||||
conditions = this.conditions.concat(deepClone(this.clipboard!.condition));
|
||||
} else {
|
||||
const condition = value as Condition["condition"];
|
||||
|
||||
const conditions = this.conditions.concat({
|
||||
condition: condition as any,
|
||||
...elClass.defaultConfig,
|
||||
});
|
||||
const elClass = customElements.get(
|
||||
`ha-automation-condition-${condition}`
|
||||
) as CustomElementConstructor & {
|
||||
defaultConfig: Omit<Condition, "condition">;
|
||||
};
|
||||
|
||||
conditions = this.conditions.concat({
|
||||
condition: condition as any,
|
||||
...elClass.defaultConfig,
|
||||
});
|
||||
}
|
||||
this._focusLastConditionOnChange = true;
|
||||
fireEvent(this, "value-changed", { value: conditions });
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import type { LogicalCondition } from "../../../../../data/automation";
|
||||
import type {
|
||||
LogicalCondition,
|
||||
Clipboard,
|
||||
} from "../../../../../data/automation";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../ha-automation-condition";
|
||||
import type { ConditionElement } from "../ha-automation-condition-row";
|
||||
@ -16,6 +19,8 @@ export class HaLogicalCondition extends LitElement implements ConditionElement {
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
public static get defaultConfig() {
|
||||
return {
|
||||
conditions: [],
|
||||
@ -30,6 +35,7 @@ export class HaLogicalCondition extends LitElement implements ConditionElement {
|
||||
@value-changed=${this._valueChanged}
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
.clipboard=${this.clipboard}
|
||||
.reOrderMode=${this.reOrderMode}
|
||||
></ha-automation-condition>
|
||||
`;
|
||||
|
@ -46,7 +46,10 @@ import {
|
||||
saveAutomationConfig,
|
||||
showAutomationEditor,
|
||||
triggerAutomationActions,
|
||||
Trigger,
|
||||
Condition,
|
||||
} from "../../../data/automation";
|
||||
import { Action } from "../../../data/script";
|
||||
import { fetchEntityRegistry } from "../../../data/entity_registry";
|
||||
import {
|
||||
showAlertDialog,
|
||||
@ -76,6 +79,11 @@ declare global {
|
||||
"ui-mode-not-available": Error;
|
||||
duplicate: undefined;
|
||||
"re-order": undefined;
|
||||
"set-clipboard": {
|
||||
trigger?: Trigger;
|
||||
condition?: Condition;
|
||||
action?: Action;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,8 @@ import "@material/mwc-button/mwc-button";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
@ -10,6 +11,7 @@ import {
|
||||
Condition,
|
||||
ManualAutomationConfig,
|
||||
Trigger,
|
||||
Clipboard,
|
||||
} from "../../../data/automation";
|
||||
import { Action } from "../../../data/script";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
@ -33,6 +35,8 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||
|
||||
@state() private _clipboard: Clipboard = {};
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
${this.disabled
|
||||
@ -91,6 +95,8 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
@value-changed=${this._triggerChanged}
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
@set-clipboard=${this._setClipboard}
|
||||
.clipboard=${this._clipboard}
|
||||
></ha-automation-trigger>
|
||||
|
||||
<div class="header">
|
||||
@ -120,6 +126,8 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
@value-changed=${this._conditionChanged}
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
@set-clipboard=${this._setClipboard}
|
||||
.clipboard=${this._clipboard}
|
||||
></ha-automation-condition>
|
||||
|
||||
<div class="header">
|
||||
@ -152,6 +160,8 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.disabled=${this.disabled}
|
||||
@set-clipboard=${this._setClipboard}
|
||||
.clipboard=${this._clipboard}
|
||||
></ha-automation-action>
|
||||
`;
|
||||
}
|
||||
@ -163,6 +173,11 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _setClipboard(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
this._clipboard = { ...this._clipboard, ...deepClone(ev.detail) };
|
||||
}
|
||||
|
||||
private _conditionChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "value-changed", {
|
||||
|
@ -3,6 +3,8 @@ import "@material/mwc-list/mwc-list-item";
|
||||
import {
|
||||
mdiCheck,
|
||||
mdiContentDuplicate,
|
||||
mdiContentCopy,
|
||||
mdiContentCut,
|
||||
mdiDelete,
|
||||
mdiDotsVertical,
|
||||
mdiIdentifier,
|
||||
@ -166,6 +168,18 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
<ha-svg-icon slot="graphic" .path=${mdiSort}></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.edit_id"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiIdentifier}
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<li divider role="separator"></li>
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.duplicate"
|
||||
@ -178,11 +192,21 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.edit_id"
|
||||
"ui.panel.config.automation.editor.triggers.copy"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiIdentifier}
|
||||
.path=${mdiContentCopy}
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.cut"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiContentCut}
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
@ -427,24 +451,31 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
fireEvent(this, "re-order");
|
||||
break;
|
||||
case 2:
|
||||
fireEvent(this, "duplicate");
|
||||
break;
|
||||
case 3:
|
||||
this._requestShowId = true;
|
||||
this.expand();
|
||||
break;
|
||||
case 3:
|
||||
fireEvent(this, "duplicate");
|
||||
break;
|
||||
case 4:
|
||||
fireEvent(this, "set-clipboard", { trigger: this.trigger });
|
||||
break;
|
||||
case 5:
|
||||
fireEvent(this, "set-clipboard", { trigger: this.trigger });
|
||||
fireEvent(this, "value-changed", { value: null });
|
||||
break;
|
||||
case 6:
|
||||
this._switchUiMode();
|
||||
this.expand();
|
||||
break;
|
||||
case 5:
|
||||
case 7:
|
||||
this._switchYamlMode();
|
||||
this.expand();
|
||||
break;
|
||||
case 6:
|
||||
case 8:
|
||||
this._onDisable();
|
||||
break;
|
||||
case 7:
|
||||
case 9:
|
||||
this._onDelete();
|
||||
break;
|
||||
}
|
||||
|
@ -1,8 +1,21 @@
|
||||
import "@material/mwc-button";
|
||||
import type { ActionDetail } from "@material/mwc-list";
|
||||
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
mdiArrowDown,
|
||||
mdiArrowUp,
|
||||
mdiDrag,
|
||||
mdiPlus,
|
||||
mdiContentPaste,
|
||||
} from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
PropertyValues,
|
||||
} from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
@ -14,7 +27,7 @@ import "../../../../components/ha-button-menu";
|
||||
import "../../../../components/ha-button";
|
||||
import type { HaSelect } from "../../../../components/ha-select";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import { Trigger } from "../../../../data/automation";
|
||||
import { Trigger, Clipboard } from "../../../../data/automation";
|
||||
import { TRIGGER_TYPES } from "../../../../data/trigger";
|
||||
import { sortableStyles } from "../../../../resources/ha-sortable-style";
|
||||
import { SortableInstance } from "../../../../resources/sortable";
|
||||
@ -38,6 +51,8 @@ import "./types/ha-automation-trigger-time_pattern";
|
||||
import "./types/ha-automation-trigger-webhook";
|
||||
import "./types/ha-automation-trigger-zone";
|
||||
|
||||
const PASTE_VALUE = "__paste__";
|
||||
|
||||
@customElement("ha-automation-trigger")
|
||||
export default class HaAutomationTrigger extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -50,6 +65,8 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public reOrderMode = false;
|
||||
|
||||
@property() public clipboard?: Clipboard;
|
||||
|
||||
private _focusLastTriggerOnChange = false;
|
||||
|
||||
private _triggerKeys = new WeakMap<Trigger, string>();
|
||||
@ -136,6 +153,22 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
|
||||
</ha-button>
|
||||
${
|
||||
this.clipboard?.trigger
|
||||
? html` <mwc-list-item .value=${PASTE_VALUE} graphic="icon">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.paste"
|
||||
)}
|
||||
(${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.triggers.type.${this.clipboard.trigger.platform}.label`
|
||||
)})
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiContentPaste}
|
||||
></ha-svg-icon
|
||||
></mwc-list-item>`
|
||||
: nothing
|
||||
}
|
||||
${this._processedTypes(this.hass.localize).map(
|
||||
([opt, label, icon]) => html`
|
||||
<mwc-list-item .value=${opt} graphic="icon">
|
||||
@ -222,19 +255,25 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
}
|
||||
|
||||
private _addTrigger(ev: CustomEvent<ActionDetail>) {
|
||||
const platform = (ev.currentTarget as HaSelect).items[ev.detail.index]
|
||||
.value as Trigger["platform"];
|
||||
const value = (ev.currentTarget as HaSelect).items[ev.detail.index].value;
|
||||
|
||||
const elClass = customElements.get(
|
||||
`ha-automation-trigger-${platform}`
|
||||
) as CustomElementConstructor & {
|
||||
defaultConfig: Omit<Trigger, "platform">;
|
||||
};
|
||||
let triggers: Trigger[];
|
||||
if (value === PASTE_VALUE) {
|
||||
triggers = this.triggers.concat(deepClone(this.clipboard!.trigger));
|
||||
} else {
|
||||
const platform = value as Trigger["platform"];
|
||||
|
||||
const triggers = this.triggers.concat({
|
||||
platform: platform as any,
|
||||
...elClass.defaultConfig,
|
||||
});
|
||||
const elClass = customElements.get(
|
||||
`ha-automation-trigger-${platform}`
|
||||
) as CustomElementConstructor & {
|
||||
defaultConfig: Omit<Trigger, "platform">;
|
||||
};
|
||||
|
||||
triggers = this.triggers.concat({
|
||||
platform: platform as any,
|
||||
...elClass.defaultConfig,
|
||||
});
|
||||
}
|
||||
this._focusLastTriggerOnChange = true;
|
||||
fireEvent(this, "value-changed", { value: triggers });
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
import { Action, ScriptConfig } from "../../../data/script";
|
||||
import { Clipboard } from "../../../data/automation";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
@ -23,6 +25,8 @@ export class HaManualScriptEditor extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public config!: ScriptConfig;
|
||||
|
||||
@state() private _clipboard: Clipboard = {};
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
${this.disabled
|
||||
@ -59,6 +63,8 @@ export class HaManualScriptEditor extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.disabled=${this.disabled}
|
||||
@set-clipboard=${this._setClipboard}
|
||||
.clipboard=${this._clipboard}
|
||||
></ha-automation-action>
|
||||
`;
|
||||
}
|
||||
@ -70,6 +76,11 @@ export class HaManualScriptEditor extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _setClipboard(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
this._clipboard = { ...this._clipboard, ...deepClone(ev.detail) };
|
||||
}
|
||||
|
||||
private _duplicate() {
|
||||
fireEvent(this, "duplicate");
|
||||
}
|
||||
|
@ -2230,6 +2230,9 @@
|
||||
"duplicate": "[%key:ui::common::duplicate%]",
|
||||
"re_order": "Re-order",
|
||||
"rename": "Rename",
|
||||
"cut": "Cut",
|
||||
"copy": "Copy",
|
||||
"paste": "Paste",
|
||||
"change_alias": "Rename trigger",
|
||||
"alias": "Trigger name",
|
||||
"delete": "[%key:ui::common::delete%]",
|
||||
@ -2359,6 +2362,9 @@
|
||||
"duplicate": "[%key:ui::common::duplicate%]",
|
||||
"re_order": "[%key:ui::panel::config::automation::editor::triggers::re_order%]",
|
||||
"rename": "[%key:ui::panel::config::automation::editor::triggers::rename%]",
|
||||
"cut": "[%key:ui::panel::config::automation::editor::triggers::cut%]",
|
||||
"copy": "[%key:ui::panel::config::automation::editor::triggers::copy%]",
|
||||
"paste": "[%key:ui::panel::config::automation::editor::triggers::paste%]",
|
||||
"change_alias": "Rename condition",
|
||||
"alias": "Condition name",
|
||||
"delete": "[%key:ui::common::delete%]",
|
||||
@ -2456,6 +2462,9 @@
|
||||
"duplicate": "[%key:ui::common::duplicate%]",
|
||||
"re_order": "[%key:ui::panel::config::automation::editor::triggers::re_order%]",
|
||||
"rename": "[%key:ui::panel::config::automation::editor::triggers::rename%]",
|
||||
"cut": "[%key:ui::panel::config::automation::editor::triggers::cut%]",
|
||||
"copy": "[%key:ui::panel::config::automation::editor::triggers::copy%]",
|
||||
"paste": "[%key:ui::panel::config::automation::editor::triggers::paste%]",
|
||||
"change_alias": "Rename action",
|
||||
"alias": "Action name",
|
||||
"enable": "Enable",
|
||||
|
Loading…
x
Reference in New Issue
Block a user