mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-17 15:00:31 +00:00
Expand pasting capabilities of automation editor (#26992)
This commit is contained in:
@@ -567,6 +567,7 @@ export interface TriggerSidebarConfig extends BaseSidebarConfig {
|
|||||||
duplicate: () => void;
|
duplicate: () => void;
|
||||||
cut: () => void;
|
cut: () => void;
|
||||||
copy: () => void;
|
copy: () => void;
|
||||||
|
insertAfter: (value: Trigger | Trigger[]) => boolean;
|
||||||
toggleYamlMode: () => void;
|
toggleYamlMode: () => void;
|
||||||
config: Trigger;
|
config: Trigger;
|
||||||
yamlMode: boolean;
|
yamlMode: boolean;
|
||||||
@@ -581,6 +582,7 @@ export interface ConditionSidebarConfig extends BaseSidebarConfig {
|
|||||||
duplicate: () => void;
|
duplicate: () => void;
|
||||||
cut: () => void;
|
cut: () => void;
|
||||||
copy: () => void;
|
copy: () => void;
|
||||||
|
insertAfter: (value: Condition | Condition[]) => boolean;
|
||||||
toggleYamlMode: () => void;
|
toggleYamlMode: () => void;
|
||||||
config: Condition;
|
config: Condition;
|
||||||
yamlMode: boolean;
|
yamlMode: boolean;
|
||||||
@@ -594,6 +596,7 @@ export interface ActionSidebarConfig extends BaseSidebarConfig {
|
|||||||
duplicate: () => void;
|
duplicate: () => void;
|
||||||
cut: () => void;
|
cut: () => void;
|
||||||
copy: () => void;
|
copy: () => void;
|
||||||
|
insertAfter: (value: Action | Action[]) => boolean;
|
||||||
run: () => void;
|
run: () => void;
|
||||||
toggleYamlMode: () => void;
|
toggleYamlMode: () => void;
|
||||||
config: {
|
config: {
|
||||||
|
|||||||
@@ -344,7 +344,11 @@ export const getActionType = (action: Action): ActionType => {
|
|||||||
if ("event" in action) {
|
if ("event" in action) {
|
||||||
return "fire_event";
|
return "fire_event";
|
||||||
}
|
}
|
||||||
if ("device_id" in action) {
|
if (
|
||||||
|
"device_id" in action &&
|
||||||
|
!("trigger" in action) &&
|
||||||
|
!("condition" in action)
|
||||||
|
) {
|
||||||
return "device_action";
|
return "device_action";
|
||||||
}
|
}
|
||||||
if ("repeat" in action) {
|
if ("repeat" in action) {
|
||||||
@@ -380,6 +384,9 @@ export const getActionType = (action: Action): ActionType => {
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isAction = (value: unknown): value is Action =>
|
||||||
|
getActionType(value as Action) !== "unknown";
|
||||||
|
|
||||||
export const hasScriptFields = (
|
export const hasScriptFields = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityId: string
|
entityId: string
|
||||||
|
|||||||
@@ -15,16 +15,19 @@ import {
|
|||||||
mdiStopCircleOutline,
|
mdiStopCircleOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
|
import { dump } from "js-yaml";
|
||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { LitElement, html, nothing } from "lit";
|
import { LitElement, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||||
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 { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
|
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||||
import "../../../../components/ha-automation-row";
|
import "../../../../components/ha-automation-row";
|
||||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
@@ -61,7 +64,7 @@ import type {
|
|||||||
NonConditionAction,
|
NonConditionAction,
|
||||||
RepeatAction,
|
RepeatAction,
|
||||||
} from "../../../../data/script";
|
} from "../../../../data/script";
|
||||||
import { getActionType } from "../../../../data/script";
|
import { getActionType, isAction } from "../../../../data/script";
|
||||||
import { describeAction } from "../../../../data/script_i18n";
|
import { describeAction } from "../../../../data/script_i18n";
|
||||||
import { callExecuteScript } from "../../../../data/service";
|
import { callExecuteScript } from "../../../../data/service";
|
||||||
import {
|
import {
|
||||||
@@ -506,6 +509,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
...this._clipboard,
|
...this._clipboard,
|
||||||
action: deepClone(this.action),
|
action: deepClone(this.action),
|
||||||
};
|
};
|
||||||
|
copyToClipboard(dump(this.action));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onDisable = () => {
|
private _onDisable = () => {
|
||||||
@@ -636,6 +640,14 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
fireEvent(this, "duplicate");
|
fireEvent(this, "duplicate");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private _insertAfter = (value: Action | Action[]) => {
|
||||||
|
if (ensureArray(value).some((val) => !isAction(val))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fireEvent(this, "insert-after", { value });
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
private _copyAction = () => {
|
private _copyAction = () => {
|
||||||
this._setClipboard();
|
this._setClipboard();
|
||||||
showToast(this, {
|
showToast(this, {
|
||||||
@@ -724,6 +736,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
copy: this._copyAction,
|
copy: this._copyAction,
|
||||||
cut: this._cutAction,
|
cut: this._cutAction,
|
||||||
duplicate: this._duplicateAction,
|
duplicate: this._duplicateAction,
|
||||||
|
insertAfter: this._insertAfter,
|
||||||
run: this._runAction,
|
run: this._runAction,
|
||||||
config: {
|
config: {
|
||||||
action: sidebarAction,
|
action: sidebarAction,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
import { automationRowsStyles } from "../styles";
|
import { automationRowsStyles } from "../styles";
|
||||||
import type HaAutomationActionRow from "./ha-automation-action-row";
|
import type HaAutomationActionRow from "./ha-automation-action-row";
|
||||||
import { getAutomationActionType } from "./ha-automation-action-row";
|
import { getAutomationActionType } from "./ha-automation-action-row";
|
||||||
|
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||||
|
|
||||||
@customElement("ha-automation-action")
|
@customElement("ha-automation-action")
|
||||||
export default class HaAutomationAction extends LitElement {
|
export default class HaAutomationAction extends LitElement {
|
||||||
@@ -92,6 +93,7 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@duplicate=${this._duplicateAction}
|
@duplicate=${this._duplicateAction}
|
||||||
|
@insert-after=${this._insertAfter}
|
||||||
@move-down=${this._moveDown}
|
@move-down=${this._moveDown}
|
||||||
@move-up=${this._moveUp}
|
@move-up=${this._moveUp}
|
||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
@@ -364,7 +366,23 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const index = (ev.target as any).index;
|
const index = (ev.target as any).index;
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: this.actions.concat(deepClone(this.actions[index])),
|
// @ts-expect-error Requires library bump to ES2023
|
||||||
|
value: this.actions.toSpliced(
|
||||||
|
index + 1,
|
||||||
|
0,
|
||||||
|
deepClone(this.actions[index])
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _insertAfter(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const index = (ev.target as any).index;
|
||||||
|
const inserted = ensureArray(ev.detail.value);
|
||||||
|
this.highlightedActions = inserted;
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
// @ts-expect-error Requires library bump to ES2023
|
||||||
|
value: this.actions.toSpliced(index + 1, 0, ...inserted),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,17 +14,20 @@ import {
|
|||||||
mdiStopCircleOutline,
|
mdiStopCircleOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
|
import { dump } from "js-yaml";
|
||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||||
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 { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
|
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||||
import "../../../../components/ha-automation-row";
|
import "../../../../components/ha-automation-row";
|
||||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
@@ -38,7 +41,7 @@ import type {
|
|||||||
Condition,
|
Condition,
|
||||||
ConditionSidebarConfig,
|
ConditionSidebarConfig,
|
||||||
} from "../../../../data/automation";
|
} from "../../../../data/automation";
|
||||||
import { testCondition } from "../../../../data/automation";
|
import { isCondition, testCondition } from "../../../../data/automation";
|
||||||
import { describeCondition } from "../../../../data/automation_i18n";
|
import { describeCondition } from "../../../../data/automation_i18n";
|
||||||
import {
|
import {
|
||||||
CONDITION_BUILDING_BLOCKS,
|
CONDITION_BUILDING_BLOCKS,
|
||||||
@@ -437,6 +440,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
...this._clipboard,
|
...this._clipboard,
|
||||||
condition: deepClone(this.condition),
|
condition: deepClone(this.condition),
|
||||||
};
|
};
|
||||||
|
copyToClipboard(dump(this.condition));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onDisable = () => {
|
private _onDisable = () => {
|
||||||
@@ -582,6 +586,14 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
fireEvent(this, "duplicate");
|
fireEvent(this, "duplicate");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private _insertAfter = (value: Condition | Condition[]) => {
|
||||||
|
if (ensureArray(value).some((val) => !isCondition(val))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fireEvent(this, "insert-after", { value });
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
private _copyCondition = () => {
|
private _copyCondition = () => {
|
||||||
this._setClipboard();
|
this._setClipboard();
|
||||||
showToast(this, {
|
showToast(this, {
|
||||||
@@ -693,6 +705,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
disable: this._onDisable,
|
disable: this._onDisable,
|
||||||
delete: this._onDelete,
|
delete: this._onDelete,
|
||||||
duplicate: this._duplicateCondition,
|
duplicate: this._duplicateCondition,
|
||||||
|
insertAfter: this._insertAfter,
|
||||||
copy: this._copyCondition,
|
copy: this._copyCondition,
|
||||||
cut: this._cutCondition,
|
cut: this._cutCondition,
|
||||||
test: this._testCondition,
|
test: this._testCondition,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
import { automationRowsStyles } from "../styles";
|
import { automationRowsStyles } from "../styles";
|
||||||
import "./ha-automation-condition-row";
|
import "./ha-automation-condition-row";
|
||||||
import type HaAutomationConditionRow from "./ha-automation-condition-row";
|
import type HaAutomationConditionRow from "./ha-automation-condition-row";
|
||||||
|
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||||
|
|
||||||
@customElement("ha-automation-condition")
|
@customElement("ha-automation-condition")
|
||||||
export default class HaAutomationCondition extends LitElement {
|
export default class HaAutomationCondition extends LitElement {
|
||||||
@@ -170,6 +171,7 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
@duplicate=${this._duplicateCondition}
|
@duplicate=${this._duplicateCondition}
|
||||||
|
@insert-after=${this._insertAfter}
|
||||||
@move-down=${this._moveDown}
|
@move-down=${this._moveDown}
|
||||||
@move-up=${this._moveUp}
|
@move-up=${this._moveUp}
|
||||||
@value-changed=${this._conditionChanged}
|
@value-changed=${this._conditionChanged}
|
||||||
@@ -383,7 +385,23 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const index = (ev.target as any).index;
|
const index = (ev.target as any).index;
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: this.conditions.concat(deepClone(this.conditions[index])),
|
// @ts-expect-error Requires library bump to ES2023
|
||||||
|
value: this.conditions.toSpliced(
|
||||||
|
index + 1,
|
||||||
|
0,
|
||||||
|
deepClone(this.conditions[index])
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _insertAfter(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const index = (ev.target as any).index;
|
||||||
|
const inserted = ensureArray(ev.detail.value);
|
||||||
|
this.highlightedConditions = inserted;
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
// @ts-expect-error Requires library bump to ES2023
|
||||||
|
value: this.conditions.toSpliced(index + 1, 0, ...inserted),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ import type {
|
|||||||
AutomationConfig,
|
AutomationConfig,
|
||||||
AutomationEntity,
|
AutomationEntity,
|
||||||
BlueprintAutomationConfig,
|
BlueprintAutomationConfig,
|
||||||
|
Condition,
|
||||||
|
Trigger,
|
||||||
} from "../../../data/automation";
|
} from "../../../data/automation";
|
||||||
import {
|
import {
|
||||||
deleteAutomation,
|
deleteAutomation,
|
||||||
@@ -60,6 +62,7 @@ import {
|
|||||||
type EntityRegistryEntry,
|
type EntityRegistryEntry,
|
||||||
updateEntityRegistryEntry,
|
updateEntityRegistryEntry,
|
||||||
} from "../../../data/entity_registry";
|
} from "../../../data/entity_registry";
|
||||||
|
import type { Action } from "../../../data/script";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
@@ -96,6 +99,9 @@ declare global {
|
|||||||
"move-down": undefined;
|
"move-down": undefined;
|
||||||
"move-up": undefined;
|
"move-up": undefined;
|
||||||
duplicate: undefined;
|
duplicate: undefined;
|
||||||
|
"insert-after": {
|
||||||
|
value: Trigger | Condition | Action | Trigger[] | Condition[] | Action[];
|
||||||
|
};
|
||||||
"save-automation": undefined;
|
"save-automation": undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import "../../../components/ha-fab";
|
|||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-markdown";
|
import "../../../components/ha-markdown";
|
||||||
import type {
|
import type {
|
||||||
ActionSidebarConfig,
|
|
||||||
AutomationConfig,
|
AutomationConfig,
|
||||||
Condition,
|
Condition,
|
||||||
ManualAutomationConfig,
|
ManualAutomationConfig,
|
||||||
@@ -172,7 +171,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="triggers-heading"
|
aria-labelledby="triggers-heading"
|
||||||
.triggers=${this.config.triggers || []}
|
.triggers=${this.config.triggers || []}
|
||||||
.highlightedTriggers=${this._pastedConfig?.triggers || []}
|
.highlightedTriggers=${this._pastedConfig?.triggers}
|
||||||
@value-changed=${this._triggerChanged}
|
@value-changed=${this._triggerChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled || this.saving}
|
.disabled=${this.disabled || this.saving}
|
||||||
@@ -219,7 +218,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="conditions-heading"
|
aria-labelledby="conditions-heading"
|
||||||
.conditions=${this.config.conditions || []}
|
.conditions=${this.config.conditions || []}
|
||||||
.highlightedConditions=${this._pastedConfig?.conditions || []}
|
.highlightedConditions=${this._pastedConfig?.conditions}
|
||||||
@value-changed=${this._conditionChanged}
|
@value-changed=${this._conditionChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled || this.saving}
|
.disabled=${this.disabled || this.saving}
|
||||||
@@ -264,7 +263,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="actions-heading"
|
aria-labelledby="actions-heading"
|
||||||
.actions=${this.config.actions || []}
|
.actions=${this.config.actions || []}
|
||||||
.highlightedActions=${this._pastedConfig?.actions || []}
|
.highlightedActions=${this._pastedConfig?.actions}
|
||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
@open-sidebar=${this._openSidebar}
|
@open-sidebar=${this._openSidebar}
|
||||||
@request-close-sidebar=${this._triggerCloseSidebar}
|
@request-close-sidebar=${this._triggerCloseSidebar}
|
||||||
@@ -518,12 +517,30 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
if (normalized) {
|
if (normalized) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
|
const keysPresent = Object.keys(normalized).filter(
|
||||||
|
(key) => ensureArray(normalized[key]).length
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
keysPresent.length === 1 &&
|
||||||
|
["triggers", "conditions", "actions"].includes(keysPresent[0])
|
||||||
|
) {
|
||||||
|
// if only one type of element is pasted, insert under the currently active item
|
||||||
|
const previousConfig = { ...this.config };
|
||||||
|
if (this._tryInsertAfterSelected(normalized[keysPresent[0]])) {
|
||||||
|
this._previousConfig = previousConfig;
|
||||||
|
this._showPastedToastWithUndo();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.dirty ||
|
this.dirty ||
|
||||||
ensureArray(this.config.triggers)?.length ||
|
ensureArray(this.config.triggers)?.length ||
|
||||||
ensureArray(this.config.conditions)?.length ||
|
ensureArray(this.config.conditions)?.length ||
|
||||||
ensureArray(this.config.actions)?.length
|
ensureArray(this.config.actions)?.length
|
||||||
) {
|
) {
|
||||||
|
// ask if they want to append or replace if we have existing config or there are unsaved changes
|
||||||
const result = await new Promise<boolean>((resolve) => {
|
const result = await new Promise<boolean>((resolve) => {
|
||||||
showPasteReplaceDialog(this, {
|
showPasteReplaceDialog(this, {
|
||||||
domain: "automation",
|
domain: "automation",
|
||||||
@@ -644,21 +661,30 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _tryInsertAfterSelected(
|
||||||
|
config: Trigger | Condition | Action | Trigger[] | Condition[] | Action[]
|
||||||
|
): boolean {
|
||||||
|
if (this._sidebarConfig && "insertAfter" in this._sidebarConfig) {
|
||||||
|
return this._sidebarConfig.insertAfter(config as any);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public copySelectedRow() {
|
public copySelectedRow() {
|
||||||
if ((this._sidebarConfig as ActionSidebarConfig)?.copy) {
|
if (this._sidebarConfig && "copy" in this._sidebarConfig) {
|
||||||
(this._sidebarConfig as ActionSidebarConfig).copy();
|
this._sidebarConfig.copy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public cutSelectedRow() {
|
public cutSelectedRow() {
|
||||||
if ((this._sidebarConfig as ActionSidebarConfig)?.cut) {
|
if (this._sidebarConfig && "cut" in this._sidebarConfig) {
|
||||||
(this._sidebarConfig as ActionSidebarConfig).cut();
|
this._sidebarConfig.cut();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteSelectedRow() {
|
public deleteSelectedRow() {
|
||||||
if ((this._sidebarConfig as ActionSidebarConfig)?.delete) {
|
if (this._sidebarConfig && "delete" in this._sidebarConfig) {
|
||||||
(this._sidebarConfig as ActionSidebarConfig).delete();
|
this._sidebarConfig.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -293,7 +293,12 @@ export default class HaAutomationOption extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const index = (ev.target as any).index;
|
const index = (ev.target as any).index;
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: this.options.concat(deepClone(this.options[index])),
|
// @ts-expect-error Requires library bump to ES2023
|
||||||
|
value: this.options.toSpliced(
|
||||||
|
index + 1,
|
||||||
|
0,
|
||||||
|
deepClone(this.options[index])
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,17 +14,20 @@ import {
|
|||||||
mdiStopCircleOutline,
|
mdiStopCircleOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
import { dump } from "js-yaml";
|
||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||||
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 { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
|
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||||
import { debounce } from "../../../../common/util/debounce";
|
import { debounce } from "../../../../common/util/debounce";
|
||||||
import "../../../../components/ha-alert";
|
import "../../../../components/ha-alert";
|
||||||
import "../../../../components/ha-automation-row";
|
import "../../../../components/ha-automation-row";
|
||||||
@@ -40,7 +43,7 @@ import type {
|
|||||||
Trigger,
|
Trigger,
|
||||||
TriggerSidebarConfig,
|
TriggerSidebarConfig,
|
||||||
} from "../../../../data/automation";
|
} from "../../../../data/automation";
|
||||||
import { subscribeTrigger } from "../../../../data/automation";
|
import { isTrigger, subscribeTrigger } from "../../../../data/automation";
|
||||||
import { describeTrigger } from "../../../../data/automation_i18n";
|
import { describeTrigger } from "../../../../data/automation_i18n";
|
||||||
import { validateConfig } from "../../../../data/config";
|
import { validateConfig } from "../../../../data/config";
|
||||||
import { fullEntitiesContext } from "../../../../data/context";
|
import { fullEntitiesContext } from "../../../../data/context";
|
||||||
@@ -508,6 +511,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
copy: this._copyTrigger,
|
copy: this._copyTrigger,
|
||||||
duplicate: this._duplicateTrigger,
|
duplicate: this._duplicateTrigger,
|
||||||
cut: this._cutTrigger,
|
cut: this._cutTrigger,
|
||||||
|
insertAfter: this._insertAfter,
|
||||||
config: trigger || this.trigger,
|
config: trigger || this.trigger,
|
||||||
uiSupported: this._uiSupported(this._getType(trigger || this.trigger)),
|
uiSupported: this._uiSupported(this._getType(trigger || this.trigger)),
|
||||||
yamlMode: this._yamlMode,
|
yamlMode: this._yamlMode,
|
||||||
@@ -529,6 +533,8 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
...this._clipboard,
|
...this._clipboard,
|
||||||
trigger: this.trigger,
|
trigger: this.trigger,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
copyToClipboard(dump(this.trigger));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onDelete = () => {
|
private _onDelete = () => {
|
||||||
@@ -636,6 +642,14 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
fireEvent(this, "duplicate");
|
fireEvent(this, "duplicate");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private _insertAfter = (value: Trigger | Trigger[]) => {
|
||||||
|
if (ensureArray(value).some((val) => !isTrigger(val))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fireEvent(this, "insert-after", { value });
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
private _copyTrigger = () => {
|
private _copyTrigger = () => {
|
||||||
this._setClipboard();
|
this._setClipboard();
|
||||||
showToast(this, {
|
showToast(this, {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
import { automationRowsStyles } from "../styles";
|
import { automationRowsStyles } from "../styles";
|
||||||
import "./ha-automation-trigger-row";
|
import "./ha-automation-trigger-row";
|
||||||
import type HaAutomationTriggerRow from "./ha-automation-trigger-row";
|
import type HaAutomationTriggerRow from "./ha-automation-trigger-row";
|
||||||
|
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||||
|
|
||||||
@customElement("ha-automation-trigger")
|
@customElement("ha-automation-trigger")
|
||||||
export default class HaAutomationTrigger extends LitElement {
|
export default class HaAutomationTrigger extends LitElement {
|
||||||
@@ -85,6 +86,7 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
.last=${idx === this.triggers.length - 1}
|
.last=${idx === this.triggers.length - 1}
|
||||||
.trigger=${trg}
|
.trigger=${trg}
|
||||||
@duplicate=${this._duplicateTrigger}
|
@duplicate=${this._duplicateTrigger}
|
||||||
|
@insert-after=${this._insertAfter}
|
||||||
@move-down=${this._moveDown}
|
@move-down=${this._moveDown}
|
||||||
@move-up=${this._moveUp}
|
@move-up=${this._moveUp}
|
||||||
@value-changed=${this._triggerChanged}
|
@value-changed=${this._triggerChanged}
|
||||||
@@ -323,7 +325,23 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const index = (ev.target as any).index;
|
const index = (ev.target as any).index;
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: this.triggers.concat(deepClone(this.triggers[index])),
|
// @ts-expect-error Requires library bump to ES2023
|
||||||
|
value: this.triggers.toSpliced(
|
||||||
|
index + 1,
|
||||||
|
0,
|
||||||
|
deepClone(this.triggers[index])
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _insertAfter(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const index = (ev.target as any).index;
|
||||||
|
const inserted = ensureArray(ev.detail.value);
|
||||||
|
this.highlightedTriggers = inserted;
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
// @ts-expect-error Requires library bump to ES2023
|
||||||
|
value: this.triggers.toSpliced(index + 1, 0, ...inserted),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="sequence-heading"
|
aria-labelledby="sequence-heading"
|
||||||
.actions=${this.config.sequence || []}
|
.actions=${this.config.sequence || []}
|
||||||
.highlightedActions=${this._pastedConfig?.sequence || []}
|
.highlightedActions=${this._pastedConfig?.sequence}
|
||||||
@value-changed=${this._sequenceChanged}
|
@value-changed=${this._sequenceChanged}
|
||||||
@open-sidebar=${this._openSidebar}
|
@open-sidebar=${this._openSidebar}
|
||||||
@request-close-sidebar=${this._triggerCloseSidebar}
|
@request-close-sidebar=${this._triggerCloseSidebar}
|
||||||
@@ -396,6 +396,20 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
if (normalized) {
|
if (normalized) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
|
const keysPresent = Object.keys(normalized).filter(
|
||||||
|
(key) => ensureArray(normalized[key]).length
|
||||||
|
);
|
||||||
|
|
||||||
|
if (keysPresent.length === 1 && ["sequence"].includes(keysPresent[0])) {
|
||||||
|
// if only one type of element is pasted, insert under the currently active item
|
||||||
|
const previousConfig = { ...this.config };
|
||||||
|
if (this._tryInsertAfterSelected(normalized[keysPresent[0]])) {
|
||||||
|
this._previousConfig = previousConfig;
|
||||||
|
this._showPastedToastWithUndo();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.dirty ||
|
this.dirty ||
|
||||||
ensureArray(this.config.sequence)?.length ||
|
ensureArray(this.config.sequence)?.length ||
|
||||||
@@ -546,6 +560,13 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
fireEvent(this, "save-script");
|
fireEvent(this, "save-script");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _tryInsertAfterSelected(config: Action | Action[]): boolean {
|
||||||
|
if (this._sidebarConfig && "insertAfter" in this._sidebarConfig) {
|
||||||
|
return this._sidebarConfig.insertAfter(config as any);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public expandAll() {
|
public expandAll() {
|
||||||
this._collapsableElements?.forEach((element) => {
|
this._collapsableElements?.forEach((element) => {
|
||||||
element.expandAll();
|
element.expandAll();
|
||||||
|
|||||||
Reference in New Issue
Block a user