mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Automation nested drag and drop (#19338)
* Add nested drag and drop for actions * Add nested drag and drop for triggers, conditions and options * Update src/panels/config/automation/action/types/ha-automation-action-choose.ts Co-authored-by: Bram Kragten <mail@bramkragten.nl> * Move object at the root level * Add support for choose option * Fix undefined container (e.g else action) * Add common nested array move function * Move item at root level for manual automation * Fix array move * Don't fallback on body * migrate blueprint and script * Add drag and drop to service control * Use context for reorder mode * Rename reorder mode functions * Fix hide menu props * Fix drag and drop for choose action --------- Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
4046534fa8
commit
7398c6ab3e
53
src/common/util/array-move.ts
Normal file
53
src/common/util/array-move.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { ItemPath } from "../../types";
|
||||||
|
|
||||||
|
function findNestedItem(
|
||||||
|
obj: any,
|
||||||
|
path: ItemPath,
|
||||||
|
createNonExistingPath?: boolean
|
||||||
|
): any {
|
||||||
|
return path.reduce((ac, p, index, array) => {
|
||||||
|
if (ac === undefined) return undefined;
|
||||||
|
if (!ac[p] && createNonExistingPath) {
|
||||||
|
const nextP = array[index + 1];
|
||||||
|
// Create object or array depending on next path
|
||||||
|
if (nextP === undefined || typeof nextP === "number") {
|
||||||
|
ac[p] = [];
|
||||||
|
} else {
|
||||||
|
ac[p] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ac[p];
|
||||||
|
}, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function nestedArrayMove<T>(
|
||||||
|
obj: T | T[],
|
||||||
|
oldIndex: number,
|
||||||
|
newIndex: number,
|
||||||
|
oldPath?: ItemPath,
|
||||||
|
newPath?: ItemPath
|
||||||
|
): T | T[] {
|
||||||
|
const newObj = Array.isArray(obj) ? [...obj] : { ...obj };
|
||||||
|
const from = oldPath ? findNestedItem(newObj, oldPath) : newObj;
|
||||||
|
const to = newPath ? findNestedItem(newObj, newPath, true) : newObj;
|
||||||
|
|
||||||
|
if (!Array.isArray(from) || !Array.isArray(to)) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = from.splice(oldIndex, 1)[0];
|
||||||
|
to.splice(newIndex, 0, item);
|
||||||
|
|
||||||
|
return newObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function arrayMove<T = any>(
|
||||||
|
array: T[],
|
||||||
|
oldIndex: number,
|
||||||
|
newIndex: number
|
||||||
|
): T[] {
|
||||||
|
const newArray = [...array];
|
||||||
|
const [item] = newArray.splice(oldIndex, 1);
|
||||||
|
newArray.splice(newIndex, 0, item);
|
||||||
|
return newArray;
|
||||||
|
}
|
@ -24,8 +24,7 @@ export class HaActionSelector extends LitElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.actions=${this.value || []}
|
.actions=${this.value || []}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.nested=${this.selector.action?.nested}
|
.path=${this.selector.action?.path}
|
||||||
.reOrderMode=${this.selector.action?.reorder_mode}
|
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,7 @@ export class HaConditionSelector extends LitElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.conditions=${this.value || []}
|
.conditions=${this.value || []}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.nested=${this.selector.condition?.nested}
|
.path=${this.selector.condition?.path}
|
||||||
.reOrderMode=${this.selector.condition?.reorder_mode}
|
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,7 @@ export class HaTriggerSelector extends LitElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.triggers=${this.value || []}
|
.triggers=${this.value || []}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.nested=${this.selector.trigger?.nested}
|
.path=${this.selector.trigger?.path}
|
||||||
.reOrderMode=${this.selector.trigger?.reorder_mode}
|
|
||||||
></ha-automation-trigger>
|
></ha-automation-trigger>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ import "./ha-service-picker";
|
|||||||
import "./ha-settings-row";
|
import "./ha-settings-row";
|
||||||
import "./ha-yaml-editor";
|
import "./ha-yaml-editor";
|
||||||
import type { HaYamlEditor } from "./ha-yaml-editor";
|
import type { HaYamlEditor } from "./ha-yaml-editor";
|
||||||
|
import { nestedArrayMove } from "../common/util/array-move";
|
||||||
|
import { ReorderModeMixin } from "../state/reorder-mode-mixin";
|
||||||
|
|
||||||
const attributeFilter = (values: any[], attribute: any) => {
|
const attributeFilter = (values: any[], attribute: any) => {
|
||||||
if (typeof attribute === "object") {
|
if (typeof attribute === "object") {
|
||||||
@ -75,7 +77,7 @@ interface ExtHassService extends Omit<HassService, "fields"> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-service-control")
|
@customElement("ha-service-control")
|
||||||
export class HaServiceControl extends LitElement {
|
export class HaServiceControl extends ReorderModeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public value?: {
|
@property({ attribute: false }) public value?: {
|
||||||
@ -439,6 +441,7 @@ export class HaServiceControl extends LitElement {
|
|||||||
allow-custom-entity
|
allow-custom-entity
|
||||||
></ha-entity-picker>`
|
></ha-entity-picker>`
|
||||||
: ""}
|
: ""}
|
||||||
|
${this._renderReorderModeAlert()}
|
||||||
${shouldRenderServiceDataYaml
|
${shouldRenderServiceDataYaml
|
||||||
? html`<ha-yaml-editor
|
? html`<ha-yaml-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -449,7 +452,23 @@ export class HaServiceControl extends LitElement {
|
|||||||
@value-changed=${this._dataChanged}
|
@value-changed=${this._dataChanged}
|
||||||
></ha-yaml-editor>`
|
></ha-yaml-editor>`
|
||||||
: filteredFields?.map((dataField) => {
|
: filteredFields?.map((dataField) => {
|
||||||
|
const selector = dataField?.selector ?? { text: undefined };
|
||||||
|
const type = Object.keys(selector)[0];
|
||||||
|
const enhancedSelector = [
|
||||||
|
"action",
|
||||||
|
"condition",
|
||||||
|
"trigger",
|
||||||
|
].includes(type)
|
||||||
|
? {
|
||||||
|
[type]: {
|
||||||
|
...selector[type],
|
||||||
|
path: [dataField.key],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: selector;
|
||||||
|
|
||||||
const showOptional = showOptionalToggle(dataField);
|
const showOptional = showOptionalToggle(dataField);
|
||||||
|
|
||||||
return dataField.selector &&
|
return dataField.selector &&
|
||||||
(!dataField.advanced ||
|
(!dataField.advanced ||
|
||||||
this.showAdvanced ||
|
this.showAdvanced ||
|
||||||
@ -488,7 +507,7 @@ export class HaServiceControl extends LitElement {
|
|||||||
(!this._value?.data ||
|
(!this._value?.data ||
|
||||||
this._value.data[dataField.key] === undefined))}
|
this._value.data[dataField.key] === undefined))}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.selector=${dataField.selector}
|
.selector=${enhancedSelector}
|
||||||
.key=${dataField.key}
|
.key=${dataField.key}
|
||||||
@value-changed=${this._serviceDataChanged}
|
@value-changed=${this._serviceDataChanged}
|
||||||
.value=${this._value?.data
|
.value=${this._value?.data
|
||||||
@ -496,12 +515,41 @@ export class HaServiceControl extends LitElement {
|
|||||||
: undefined}
|
: undefined}
|
||||||
.placeholder=${dataField.default}
|
.placeholder=${dataField.default}
|
||||||
.localizeValue=${this._localizeValueCallback}
|
.localizeValue=${this._localizeValueCallback}
|
||||||
|
@item-moved=${this._itemMoved}
|
||||||
></ha-selector>
|
></ha-selector>
|
||||||
</ha-settings-row>`
|
</ha-settings-row>`
|
||||||
: "";
|
: "";
|
||||||
})}`;
|
})}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderReorderModeAlert() {
|
||||||
|
if (!this._reorderMode.active) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-alert
|
||||||
|
class="re-order"
|
||||||
|
alert-type="info"
|
||||||
|
.title=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.title"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.description_all"
|
||||||
|
)}
|
||||||
|
<ha-button slot="action" @click=${this._exitReOrderMode}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.exit"
|
||||||
|
)}
|
||||||
|
</ha-button>
|
||||||
|
</ha-alert>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _exitReOrderMode() {
|
||||||
|
this._reorderMode.exit();
|
||||||
|
}
|
||||||
|
|
||||||
private _localizeValueCallback = (key: string) => {
|
private _localizeValueCallback = (key: string) => {
|
||||||
if (!this._value?.service) {
|
if (!this._value?.service) {
|
||||||
return "";
|
return "";
|
||||||
@ -697,6 +745,22 @@ export class HaServiceControl extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _itemMoved(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const { oldIndex, newIndex, oldPath, newPath } = ev.detail;
|
||||||
|
|
||||||
|
const data = this.value?.data ?? {};
|
||||||
|
|
||||||
|
const newData = nestedArrayMove(data, oldIndex, newIndex, oldPath, newPath);
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.value,
|
||||||
|
data: newData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _dataChanged(ev: CustomEvent) {
|
private _dataChanged(ev: CustomEvent) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (!ev.detail.isValid) {
|
if (!ev.detail.isValid) {
|
||||||
|
@ -4,12 +4,15 @@ import { customElement, property } from "lit/decorators";
|
|||||||
import type { SortableEvent } from "sortablejs";
|
import type { SortableEvent } from "sortablejs";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import type { SortableInstance } from "../resources/sortable";
|
import type { SortableInstance } from "../resources/sortable";
|
||||||
|
import { ItemPath } from "../types";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"item-moved": {
|
"item-moved": {
|
||||||
oldIndex: number;
|
oldIndex: number;
|
||||||
newIndex: number;
|
newIndex: number;
|
||||||
|
oldPath?: ItemPath;
|
||||||
|
newPath?: ItemPath;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,6 +24,9 @@ export class HaSortable extends LitElement {
|
|||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
public disabled = false;
|
public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
public path?: ItemPath;
|
||||||
|
|
||||||
@property({ type: Boolean, attribute: "no-style" })
|
@property({ type: Boolean, attribute: "no-style" })
|
||||||
public noStyle: boolean = false;
|
public noStyle: boolean = false;
|
||||||
|
|
||||||
@ -30,6 +36,9 @@ export class HaSortable extends LitElement {
|
|||||||
@property({ type: String, attribute: "handle-selector" })
|
@property({ type: String, attribute: "handle-selector" })
|
||||||
public handleSelector?: string;
|
public handleSelector?: string;
|
||||||
|
|
||||||
|
@property({ type: String, attribute: "group" })
|
||||||
|
public group?: string;
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues<this>) {
|
protected updated(changedProperties: PropertyValues<this>) {
|
||||||
if (changedProperties.has("disabled")) {
|
if (changedProperties.has("disabled")) {
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
@ -100,6 +109,7 @@ export class HaSortable extends LitElement {
|
|||||||
|
|
||||||
const options: SortableInstance.Options = {
|
const options: SortableInstance.Options = {
|
||||||
animation: 150,
|
animation: 150,
|
||||||
|
swapThreshold: 0.75,
|
||||||
onChoose: this._handleChoose,
|
onChoose: this._handleChoose,
|
||||||
onEnd: this._handleEnd,
|
onEnd: this._handleEnd,
|
||||||
};
|
};
|
||||||
@ -110,27 +120,41 @@ export class HaSortable extends LitElement {
|
|||||||
if (this.handleSelector) {
|
if (this.handleSelector) {
|
||||||
options.handle = this.handleSelector;
|
options.handle = this.handleSelector;
|
||||||
}
|
}
|
||||||
|
if (this.draggableSelector) {
|
||||||
|
options.draggable = this.draggableSelector;
|
||||||
|
}
|
||||||
|
if (this.group) {
|
||||||
|
options.group = this.group;
|
||||||
|
}
|
||||||
|
|
||||||
this._sortable = new Sortable(container, options);
|
this._sortable = new Sortable(container, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleEnd = (evt: SortableEvent) => {
|
private _handleEnd = async (evt: SortableEvent) => {
|
||||||
// put back in original location
|
// put back in original location
|
||||||
if ((evt.item as any).placeholder) {
|
if ((evt.item as any).placeholder) {
|
||||||
(evt.item as any).placeholder.replaceWith(evt.item);
|
(evt.item as any).placeholder.replaceWith(evt.item);
|
||||||
delete (evt.item as any).placeholder;
|
delete (evt.item as any).placeholder;
|
||||||
}
|
}
|
||||||
// if item was not moved, ignore
|
|
||||||
|
const oldIndex = evt.oldIndex;
|
||||||
|
const oldPath = (evt.from.parentElement as HaSortable).path;
|
||||||
|
const newIndex = evt.newIndex;
|
||||||
|
const newPath = (evt.to.parentElement as HaSortable).path;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
evt.oldIndex === undefined ||
|
oldIndex === undefined ||
|
||||||
evt.newIndex === undefined ||
|
newIndex === undefined ||
|
||||||
evt.oldIndex === evt.newIndex
|
(oldIndex === newIndex && oldPath?.join(".") === newPath?.join("."))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEvent(this, "item-moved", {
|
fireEvent(this, "item-moved", {
|
||||||
oldIndex: evt.oldIndex!,
|
oldIndex,
|
||||||
newIndex: evt.newIndex!,
|
newIndex,
|
||||||
|
oldPath,
|
||||||
|
newPath,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { ensureArray } from "../common/array/ensure-array";
|
|||||||
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
||||||
import { supportsFeature } from "../common/entity/supports-feature";
|
import { supportsFeature } from "../common/entity/supports-feature";
|
||||||
import { UiAction } from "../panels/lovelace/components/hui-action-editor";
|
import { UiAction } from "../panels/lovelace/components/hui-action-editor";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant, ItemPath } from "../types";
|
||||||
import {
|
import {
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
getDeviceIntegrationLookup,
|
getDeviceIntegrationLookup,
|
||||||
@ -59,8 +59,7 @@ export type Selector =
|
|||||||
|
|
||||||
export interface ActionSelector {
|
export interface ActionSelector {
|
||||||
action: {
|
action: {
|
||||||
reorder_mode?: boolean;
|
path?: ItemPath;
|
||||||
nested?: boolean;
|
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,8 +112,7 @@ export interface ColorTempSelector {
|
|||||||
|
|
||||||
export interface ConditionSelector {
|
export interface ConditionSelector {
|
||||||
condition: {
|
condition: {
|
||||||
reorder_mode?: boolean;
|
path?: ItemPath;
|
||||||
nested?: boolean;
|
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,8 +390,7 @@ export interface TimeSelector {
|
|||||||
|
|
||||||
export interface TriggerSelector {
|
export interface TriggerSelector {
|
||||||
trigger: {
|
trigger: {
|
||||||
reorder_mode?: boolean;
|
path?: ItemPath;
|
||||||
nested?: boolean;
|
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,11 @@ import {
|
|||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../../dialogs/generic/show-dialog-box";
|
} from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import {
|
||||||
|
ReorderMode,
|
||||||
|
reorderModeContext,
|
||||||
|
} from "../../../../state/reorder-mode-mixin";
|
||||||
|
import type { HomeAssistant, ItemPath } from "../../../../types";
|
||||||
import { showToast } from "../../../../util/toast";
|
import { showToast } from "../../../../util/toast";
|
||||||
import "./types/ha-automation-action-activate_scene";
|
import "./types/ha-automation-action-activate_scene";
|
||||||
import "./types/ha-automation-action-choose";
|
import "./types/ha-automation-action-choose";
|
||||||
@ -129,7 +133,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public hideMenu = false;
|
@property({ type: Boolean }) public hideMenu = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@property() public path?: ItemPath;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "automationClipboard",
|
key: "automationClipboard",
|
||||||
@ -143,6 +147,10 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: reorderModeContext, subscribe: true })
|
||||||
|
private _reorderMode?: ReorderMode;
|
||||||
|
|
||||||
@state() private _warnings?: string[];
|
@state() private _warnings?: string[];
|
||||||
|
|
||||||
@state() private _uiModeAvailable = true;
|
@state() private _uiModeAvailable = true;
|
||||||
@ -176,9 +184,13 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
|
if (!this.action) return nothing;
|
||||||
|
|
||||||
const type = getType(this.action);
|
const type = getType(this.action);
|
||||||
const yamlMode = this._yamlMode;
|
const yamlMode = this._yamlMode;
|
||||||
|
|
||||||
|
const noReorderModeAvailable = this._reorderMode === undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card outlined>
|
<ha-card outlined>
|
||||||
${this.action.enabled === false
|
${this.action.enabled === false
|
||||||
@ -247,7 +259,12 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
.path=${mdiRenameBox}
|
.path=${mdiRenameBox}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
<mwc-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
class=${classMap({ hidden: noReorderModeAvailable })}
|
||||||
|
?aria-hidden=${noReorderModeAvailable}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.re_order"
|
"ui.panel.config.automation.editor.actions.re_order"
|
||||||
)}
|
)}
|
||||||
@ -405,8 +422,8 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
action: this.action,
|
action: this.action,
|
||||||
narrow: this.narrow,
|
narrow: this.narrow,
|
||||||
reOrderMode: this.reOrderMode,
|
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
|
path: this.path,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
@ -435,7 +452,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
await this._renameAction();
|
await this._renameAction();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
fireEvent(this, "re-order");
|
this._reorderMode?.enter();
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
fireEvent(this, "duplicate");
|
fireEvent(this, "duplicate");
|
||||||
@ -640,6 +657,9 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
mwc-list-item[disabled] {
|
mwc-list-item[disabled] {
|
||||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||||
}
|
}
|
||||||
|
mwc-list-item.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.warning ul {
|
.warning ul {
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,23 @@
|
|||||||
|
import { consume } from "@lit-labs/context";
|
||||||
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
|
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
|
||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
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 { nestedArrayMove } from "../../../../common/util/array-move";
|
||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-svg-icon";
|
|
||||||
import "../../../../components/ha-sortable";
|
import "../../../../components/ha-sortable";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
import { getService, isService } from "../../../../data/action";
|
import { getService, isService } from "../../../../data/action";
|
||||||
import type { AutomationClipboard } from "../../../../data/automation";
|
import type { AutomationClipboard } from "../../../../data/automation";
|
||||||
import { Action } from "../../../../data/script";
|
import { Action } from "../../../../data/script";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import {
|
||||||
|
ReorderMode,
|
||||||
|
reorderModeContext,
|
||||||
|
} from "../../../../state/reorder-mode-mixin";
|
||||||
|
import { HomeAssistant, ItemPath } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
PASTE_VALUE,
|
PASTE_VALUE,
|
||||||
showAddAutomationElementDialog,
|
showAddAutomationElementDialog,
|
||||||
@ -27,11 +33,13 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public nested = false;
|
@property() public path?: ItemPath;
|
||||||
|
|
||||||
@property() public actions!: Action[];
|
@property() public actions!: Action[];
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@state()
|
||||||
|
@consume({ context: reorderModeContext, subscribe: true })
|
||||||
|
private _reorderMode?: ReorderMode;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "automationClipboard",
|
key: "automationClipboard",
|
||||||
@ -45,31 +53,18 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
|
|
||||||
private _actionKeys = new WeakMap<Action, string>();
|
private _actionKeys = new WeakMap<Action, string>();
|
||||||
|
|
||||||
|
private get nested() {
|
||||||
|
return this.path !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
${this.reOrderMode && !this.nested
|
|
||||||
? html`
|
|
||||||
<ha-alert
|
|
||||||
alert-type="info"
|
|
||||||
.title=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.re_order_mode.title"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.re_order_mode.description_actions"
|
|
||||||
)}
|
|
||||||
<ha-button slot="action" @click=${this._exitReOrderMode}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.re_order_mode.exit"
|
|
||||||
)}
|
|
||||||
</ha-button>
|
|
||||||
</ha-alert>
|
|
||||||
`
|
|
||||||
: null}
|
|
||||||
<ha-sortable
|
<ha-sortable
|
||||||
handle-selector=".handle"
|
handle-selector=".handle"
|
||||||
.disabled=${!this.reOrderMode}
|
.disabled=${!this._reorderMode?.active}
|
||||||
@item-moved=${this._actionMoved}
|
@item-moved=${this._actionMoved}
|
||||||
|
group="actions"
|
||||||
|
.path=${this.path}
|
||||||
>
|
>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
${repeat(
|
${repeat(
|
||||||
@ -77,18 +72,17 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
(action) => this._getKey(action),
|
(action) => this._getKey(action),
|
||||||
(action, idx) => html`
|
(action, idx) => html`
|
||||||
<ha-automation-action-row
|
<ha-automation-action-row
|
||||||
|
.path=${[...(this.path ?? []), idx]}
|
||||||
.index=${idx}
|
.index=${idx}
|
||||||
.action=${action}
|
.action=${action}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.hideMenu=${this.reOrderMode}
|
.hideMenu=${Boolean(this._reorderMode?.active)}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
@duplicate=${this._duplicateAction}
|
@duplicate=${this._duplicateAction}
|
||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
@re-order=${this._enterReOrderMode}
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
>
|
>
|
||||||
${this.reOrderMode
|
${this._reorderMode?.active
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.index=${idx}
|
.index=${idx}
|
||||||
@ -199,16 +193,6 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
fireEvent(this, "value-changed", { value: actions });
|
fireEvent(this, "value-changed", { value: actions });
|
||||||
};
|
};
|
||||||
|
|
||||||
private async _enterReOrderMode(ev: CustomEvent) {
|
|
||||||
if (this.nested) return;
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.reOrderMode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _exitReOrderMode() {
|
|
||||||
this.reOrderMode = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getKey(action: Action) {
|
private _getKey(action: Action) {
|
||||||
if (!this._actionKeys.has(action)) {
|
if (!this._actionKeys.has(action)) {
|
||||||
this._actionKeys.set(action, Math.random().toString());
|
this._actionKeys.set(action, Math.random().toString());
|
||||||
@ -229,17 +213,28 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
this._move(index, newIndex);
|
this._move(index, newIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _move(index: number, newIndex: number) {
|
private _move(
|
||||||
const actions = this.actions.concat();
|
oldIndex: number,
|
||||||
const action = actions.splice(index, 1)[0];
|
newIndex: number,
|
||||||
actions.splice(newIndex, 0, action);
|
oldPath?: ItemPath,
|
||||||
|
newPath?: ItemPath
|
||||||
|
) {
|
||||||
|
const actions = nestedArrayMove(
|
||||||
|
this.actions,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
oldPath,
|
||||||
|
newPath
|
||||||
|
);
|
||||||
|
|
||||||
fireEvent(this, "value-changed", { value: actions });
|
fireEvent(this, "value-changed", { value: actions });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _actionMoved(ev: CustomEvent): void {
|
private _actionMoved(ev: CustomEvent): void {
|
||||||
|
if (this.nested) return;
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const { oldIndex, newIndex } = ev.detail;
|
const { oldIndex, newIndex, oldPath, newPath } = ev.detail;
|
||||||
this._move(oldIndex, newIndex);
|
this._move(oldIndex, newIndex, oldPath, newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _actionChanged(ev: CustomEvent) {
|
private _actionChanged(ev: CustomEvent) {
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import { ensureArray } from "../../../../../common/array/ensure-array";
|
import { ensureArray } from "../../../../../common/array/ensure-array";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
@ -36,7 +37,11 @@ import {
|
|||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../../../dialogs/generic/show-dialog-box";
|
} from "../../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../../resources/styles";
|
import { haStyle } from "../../../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../../../types";
|
import {
|
||||||
|
ReorderMode,
|
||||||
|
reorderModeContext,
|
||||||
|
} from "../../../../../state/reorder-mode-mixin";
|
||||||
|
import { HomeAssistant, ItemPath } from "../../../../../types";
|
||||||
import { ActionElement } from "../ha-automation-action-row";
|
import { ActionElement } from "../ha-automation-action-row";
|
||||||
|
|
||||||
const preventDefault = (ev) => ev.preventDefault();
|
const preventDefault = (ev) => ev.preventDefault();
|
||||||
@ -47,9 +52,9 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property() public action!: ChooseAction;
|
@property({ attribute: false }) public path?: ItemPath;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@property() public action!: ChooseAction;
|
||||||
|
|
||||||
@state() private _showDefault = false;
|
@state() private _showDefault = false;
|
||||||
|
|
||||||
@ -59,6 +64,10 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: reorderModeContext, subscribe: true })
|
||||||
|
private _reorderMode?: ReorderMode;
|
||||||
|
|
||||||
private _expandLast = false;
|
private _expandLast = false;
|
||||||
|
|
||||||
public static get defaultConfig() {
|
public static get defaultConfig() {
|
||||||
@ -95,11 +104,14 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
protected render() {
|
protected render() {
|
||||||
const action = this.action;
|
const action = this.action;
|
||||||
|
|
||||||
|
const noReorderModeAvailable = this._reorderMode === undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-sortable
|
<ha-sortable
|
||||||
handle-selector=".handle"
|
handle-selector=".handle"
|
||||||
.disabled=${!this.reOrderMode}
|
.disabled=${!this._reorderMode?.active}
|
||||||
@item-moved=${this._optionMoved}
|
group="choose-options"
|
||||||
|
.path=${[...(this.path ?? []), "choose"]}
|
||||||
>
|
>
|
||||||
<div class="options">
|
<div class="options">
|
||||||
${repeat(
|
${repeat(
|
||||||
@ -123,7 +135,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
? ""
|
? ""
|
||||||
: this._getDescription(option))}
|
: this._getDescription(option))}
|
||||||
</h3>
|
</h3>
|
||||||
${this.reOrderMode
|
${this._reorderMode?.active
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.index=${idx}
|
.index=${idx}
|
||||||
@ -178,6 +190,10 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
<mwc-list-item
|
<mwc-list-item
|
||||||
graphic="icon"
|
graphic="icon"
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
|
class=${classMap({
|
||||||
|
hidden: noReorderModeAvailable,
|
||||||
|
})}
|
||||||
|
?aria-hidden=${noReorderModeAvailable}
|
||||||
>
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.re_order"
|
"ui.panel.config.automation.editor.actions.re_order"
|
||||||
@ -224,11 +240,15 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
)}:
|
)}:
|
||||||
</h4>
|
</h4>
|
||||||
<ha-automation-condition
|
<ha-automation-condition
|
||||||
nested
|
.path=${[
|
||||||
|
...(this.path ?? []),
|
||||||
|
"choose",
|
||||||
|
idx,
|
||||||
|
"conditions",
|
||||||
|
]}
|
||||||
.conditions=${ensureArray<string | Condition>(
|
.conditions=${ensureArray<string | Condition>(
|
||||||
option.conditions
|
option.conditions
|
||||||
)}
|
)}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.idx=${idx}
|
.idx=${idx}
|
||||||
@ -240,9 +260,13 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
)}:
|
)}:
|
||||||
</h4>
|
</h4>
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
nested
|
.path=${[
|
||||||
|
...(this.path ?? []),
|
||||||
|
"choose",
|
||||||
|
idx,
|
||||||
|
"sequence",
|
||||||
|
]}
|
||||||
.actions=${ensureArray(option.sequence) || []}
|
.actions=${ensureArray(option.sequence) || []}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.idx=${idx}
|
.idx=${idx}
|
||||||
@ -274,9 +298,8 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
)}:
|
)}:
|
||||||
</h2>
|
</h2>
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
nested
|
.path=${[...(this.path ?? []), "choose", "default"]}
|
||||||
.actions=${ensureArray(action.default) || []}
|
.actions=${ensureArray(action.default) || []}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._defaultChanged}
|
@value-changed=${this._defaultChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -302,7 +325,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
await this._renameAction(ev);
|
await this._renameAction(ev);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
fireEvent(this, "re-order");
|
this._reorderMode?.enter();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
this._duplicateOption(ev);
|
this._duplicateOption(ev);
|
||||||
@ -435,12 +458,6 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _optionMoved(ev: CustomEvent): void {
|
|
||||||
ev.stopPropagation();
|
|
||||||
const { oldIndex, newIndex } = ev.detail;
|
|
||||||
this._move(oldIndex, newIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _removeOption(ev: CustomEvent) {
|
private _removeOption(ev: CustomEvent) {
|
||||||
const index = (ev.target as any).idx;
|
const index = (ev.target as any).idx;
|
||||||
showConfirmationDialog(this, {
|
showConfirmationDialog(this, {
|
||||||
@ -495,6 +512,12 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
|||||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
--expansion-panel-summary-padding: 0 0 0 8px;
|
||||||
--expansion-panel-content-padding: 0;
|
--expansion-panel-content-padding: 0;
|
||||||
}
|
}
|
||||||
|
mwc-list-item[disabled] {
|
||||||
|
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||||
|
}
|
||||||
|
mwc-list-item.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
h3 {
|
h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
|
@ -4,7 +4,7 @@ import { fireEvent } from "../../../../../common/dom/fire_event";
|
|||||||
import "../../../../../components/ha-textfield";
|
import "../../../../../components/ha-textfield";
|
||||||
import { Action, IfAction } from "../../../../../data/script";
|
import { Action, IfAction } from "../../../../../data/script";
|
||||||
import { haStyle } from "../../../../../resources/styles";
|
import { haStyle } from "../../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant, ItemPath } from "../../../../../types";
|
||||||
import type { Condition } from "../../../../lovelace/common/validate-condition";
|
import type { Condition } from "../../../../lovelace/common/validate-condition";
|
||||||
import "../ha-automation-action";
|
import "../ha-automation-action";
|
||||||
import type { ActionElement } from "../ha-automation-action-row";
|
import type { ActionElement } from "../ha-automation-action-row";
|
||||||
@ -15,9 +15,9 @@ export class HaIfAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property({ attribute: false }) public action!: IfAction;
|
@property({ attribute: false }) public path?: ItemPath;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@property({ attribute: false }) public action!: IfAction;
|
||||||
|
|
||||||
@state() private _showElse = false;
|
@state() private _showElse = false;
|
||||||
|
|
||||||
@ -38,9 +38,8 @@ export class HaIfAction extends LitElement implements ActionElement {
|
|||||||
)}*:
|
)}*:
|
||||||
</h3>
|
</h3>
|
||||||
<ha-automation-condition
|
<ha-automation-condition
|
||||||
nested
|
.path=${[...(this.path ?? []), "if"]}
|
||||||
.conditions=${action.if}
|
.conditions=${action.if}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._ifChanged}
|
@value-changed=${this._ifChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -52,9 +51,8 @@ export class HaIfAction extends LitElement implements ActionElement {
|
|||||||
)}*:
|
)}*:
|
||||||
</h3>
|
</h3>
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
nested
|
.path=${[...(this.path ?? []), "then"]}
|
||||||
.actions=${action.then}
|
.actions=${action.then}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._thenChanged}
|
@value-changed=${this._thenChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -67,9 +65,8 @@ export class HaIfAction extends LitElement implements ActionElement {
|
|||||||
)}:
|
)}:
|
||||||
</h3>
|
</h3>
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
nested
|
.path=${[...(this.path ?? []), "else"]}
|
||||||
.actions=${action.else || []}
|
.actions=${action.else || []}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._elseChanged}
|
@value-changed=${this._elseChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@ -4,7 +4,7 @@ import { fireEvent } from "../../../../../common/dom/fire_event";
|
|||||||
import "../../../../../components/ha-textfield";
|
import "../../../../../components/ha-textfield";
|
||||||
import { Action, ParallelAction } from "../../../../../data/script";
|
import { Action, ParallelAction } from "../../../../../data/script";
|
||||||
import { haStyle } from "../../../../../resources/styles";
|
import { haStyle } from "../../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant, ItemPath } from "../../../../../types";
|
||||||
import "../ha-automation-action";
|
import "../ha-automation-action";
|
||||||
import type { ActionElement } from "../ha-automation-action-row";
|
import type { ActionElement } from "../ha-automation-action-row";
|
||||||
|
|
||||||
@ -14,9 +14,9 @@ export class HaParallelAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property({ attribute: false }) public action!: ParallelAction;
|
@property({ attribute: false }) public path?: ItemPath;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@property({ attribute: false }) public action!: ParallelAction;
|
||||||
|
|
||||||
public static get defaultConfig() {
|
public static get defaultConfig() {
|
||||||
return {
|
return {
|
||||||
@ -29,9 +29,8 @@ export class HaParallelAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
nested
|
.path=${[...(this.path ?? []), "parallel"]}
|
||||||
.actions=${action.parallel}
|
.actions=${action.parallel}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._actionsChanged}
|
@value-changed=${this._actionsChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@ -5,14 +5,17 @@ import { fireEvent } from "../../../../../common/dom/fire_event";
|
|||||||
import "../../../../../components/ha-textfield";
|
import "../../../../../components/ha-textfield";
|
||||||
import { RepeatAction } from "../../../../../data/script";
|
import { RepeatAction } from "../../../../../data/script";
|
||||||
import { haStyle } from "../../../../../resources/styles";
|
import { haStyle } from "../../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant, ItemPath } from "../../../../../types";
|
||||||
import "../ha-automation-action";
|
import "../ha-automation-action";
|
||||||
import type { ActionElement } from "../ha-automation-action-row";
|
import type { ActionElement } from "../ha-automation-action-row";
|
||||||
|
|
||||||
import { isTemplate } from "../../../../../common/string/has-template";
|
import { isTemplate } from "../../../../../common/string/has-template";
|
||||||
import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
||||||
import "../../../../../components/ha-form/ha-form";
|
import "../../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
import type {
|
||||||
|
HaFormSchema,
|
||||||
|
SchemaUnion,
|
||||||
|
} from "../../../../../components/ha-form/types";
|
||||||
|
|
||||||
const OPTIONS = ["count", "while", "until", "for_each"] as const;
|
const OPTIONS = ["count", "while", "until", "for_each"] as const;
|
||||||
|
|
||||||
@ -26,7 +29,7 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public action!: RepeatAction;
|
@property({ attribute: false }) public action!: RepeatAction;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@property() public path?: ItemPath;
|
||||||
|
|
||||||
public static get defaultConfig() {
|
public static get defaultConfig() {
|
||||||
return { repeat: { count: 2, sequence: [] } };
|
return { repeat: { count: 2, sequence: [] } };
|
||||||
@ -36,8 +39,8 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
(
|
(
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
type: string,
|
type: string,
|
||||||
reOrderMode: boolean,
|
template: boolean,
|
||||||
template: boolean
|
path?: ItemPath
|
||||||
) =>
|
) =>
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -60,20 +63,22 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
name: "count",
|
name: "count",
|
||||||
required: true,
|
required: true,
|
||||||
selector: template
|
selector: template
|
||||||
? ({ template: {} } as const)
|
? { template: {} }
|
||||||
: ({ number: { mode: "box", min: 1 } } as const),
|
: { number: { mode: "box", min: 1 } },
|
||||||
},
|
},
|
||||||
] as const)
|
] as const satisfies readonly HaFormSchema[])
|
||||||
: []),
|
: []),
|
||||||
...(type === "until" || type === "while"
|
...(type === "until" || type === "while"
|
||||||
? ([
|
? ([
|
||||||
{
|
{
|
||||||
name: type,
|
name: type,
|
||||||
selector: {
|
selector: {
|
||||||
condition: { nested: true, reorder_mode: reOrderMode },
|
condition: {
|
||||||
|
path: [...(path ?? []), "repeat", type],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as const)
|
] as const satisfies readonly HaFormSchema[])
|
||||||
: []),
|
: []),
|
||||||
...(type === "for_each"
|
...(type === "for_each"
|
||||||
? ([
|
? ([
|
||||||
@ -82,13 +87,17 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
required: true,
|
required: true,
|
||||||
selector: { object: {} },
|
selector: { object: {} },
|
||||||
},
|
},
|
||||||
] as const)
|
] as const satisfies readonly HaFormSchema[])
|
||||||
: []),
|
: []),
|
||||||
{
|
{
|
||||||
name: "sequence",
|
name: "sequence",
|
||||||
selector: { action: { nested: true, reorder_mode: reOrderMode } },
|
selector: {
|
||||||
|
action: {
|
||||||
|
path: [...(path ?? []), "repeat", "sequence"],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
] as const
|
] as const satisfies readonly HaFormSchema[]
|
||||||
);
|
);
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
@ -97,11 +106,12 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
|||||||
const schema = this._schema(
|
const schema = this._schema(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
type ?? "count",
|
type ?? "count",
|
||||||
this.reOrderMode,
|
|
||||||
"count" in action && typeof action.count === "string"
|
"count" in action && typeof action.count === "string"
|
||||||
? isTemplate(action.count)
|
? isTemplate(action.count)
|
||||||
: false
|
: false,
|
||||||
|
this.path
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = { ...action, type };
|
const data = { ...action, type };
|
||||||
return html`<ha-form
|
return html`<ha-form
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@ -8,7 +8,7 @@ import "../../../../../components/ha-duration-input";
|
|||||||
import "../../../../../components/ha-formfield";
|
import "../../../../../components/ha-formfield";
|
||||||
import "../../../../../components/ha-textfield";
|
import "../../../../../components/ha-textfield";
|
||||||
import { WaitForTriggerAction } from "../../../../../data/script";
|
import { WaitForTriggerAction } from "../../../../../data/script";
|
||||||
import { HomeAssistant } from "../../../../../types";
|
import { HomeAssistant, ItemPath } from "../../../../../types";
|
||||||
import "../../trigger/ha-automation-trigger";
|
import "../../trigger/ha-automation-trigger";
|
||||||
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ export class HaWaitForTriggerAction
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@property({ attribute: false }) public path?: ItemPath;
|
||||||
|
|
||||||
public static get defaultConfig() {
|
public static get defaultConfig() {
|
||||||
return { wait_for_trigger: [] };
|
return { wait_for_trigger: [] };
|
||||||
@ -55,12 +55,11 @@ export class HaWaitForTriggerAction
|
|||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-automation-trigger
|
<ha-automation-trigger
|
||||||
nested
|
.path=${[...(this.path ?? []), "wait_for_trigger"]}
|
||||||
.triggers=${ensureArray(this.action.wait_for_trigger)}
|
.triggers=${ensureArray(this.action.wait_for_trigger)}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.name=${"wait_for_trigger"}
|
.name=${"wait_for_trigger"}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-automation-trigger>
|
></ha-automation-trigger>
|
||||||
`;
|
`;
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { nestedArrayMove } from "../../../common/util/array-move";
|
||||||
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-blueprint-picker";
|
import "../../../components/ha-blueprint-picker";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-circular-progress";
|
import "../../../components/ha-circular-progress";
|
||||||
import "../../../components/ha-markdown";
|
import "../../../components/ha-markdown";
|
||||||
import "../../../components/ha-selector/ha-selector";
|
import "../../../components/ha-selector/ha-selector";
|
||||||
import "../../../components/ha-settings-row";
|
import "../../../components/ha-settings-row";
|
||||||
import "../../../components/ha-alert";
|
|
||||||
import { BlueprintAutomationConfig } from "../../../data/automation";
|
import { BlueprintAutomationConfig } from "../../../data/automation";
|
||||||
import {
|
import {
|
||||||
BlueprintOrError,
|
BlueprintOrError,
|
||||||
@ -17,11 +18,12 @@ import {
|
|||||||
fetchBlueprints,
|
fetchBlueprints,
|
||||||
} from "../../../data/blueprint";
|
} from "../../../data/blueprint";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
import { ReorderModeMixin } from "../../../state/reorder-mode-mixin";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import "../ha-config-section";
|
import "../ha-config-section";
|
||||||
|
|
||||||
@customElement("blueprint-automation-editor")
|
@customElement("blueprint-automation-editor")
|
||||||
export class HaBlueprintAutomationEditor extends LitElement {
|
export class HaBlueprintAutomationEditor extends ReorderModeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean }) public isWide = false;
|
@property({ type: Boolean }) public isWide = false;
|
||||||
@ -76,6 +78,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
${this.config.description
|
${this.config.description
|
||||||
? html`<p class="description">${this.config.description}</p>`
|
? html`<p class="description">${this.config.description}</p>`
|
||||||
: ""}
|
: ""}
|
||||||
|
${this._renderReorderModeAlert()}
|
||||||
<ha-card
|
<ha-card
|
||||||
outlined
|
outlined
|
||||||
class="blueprint"
|
class="blueprint"
|
||||||
@ -119,8 +122,23 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
${blueprint?.metadata?.input &&
|
${blueprint?.metadata?.input &&
|
||||||
Object.keys(blueprint.metadata.input).length
|
Object.keys(blueprint.metadata.input).length
|
||||||
? Object.entries(blueprint.metadata.input).map(
|
? Object.entries(blueprint.metadata.input).map(
|
||||||
([key, value]) =>
|
([key, value]) => {
|
||||||
html`<ha-settings-row .narrow=${this.narrow}>
|
const selector = value?.selector ?? { text: undefined };
|
||||||
|
const type = Object.keys(selector)[0];
|
||||||
|
const enhancedSelector = [
|
||||||
|
"action",
|
||||||
|
"condition",
|
||||||
|
"trigger",
|
||||||
|
].includes(type)
|
||||||
|
? {
|
||||||
|
[type]: {
|
||||||
|
...selector[type],
|
||||||
|
path: [key],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: selector;
|
||||||
|
|
||||||
|
return html`<ha-settings-row .narrow=${this.narrow}>
|
||||||
<span slot="heading">${value?.name || key}</span>
|
<span slot="heading">${value?.name || key}</span>
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
slot="description"
|
slot="description"
|
||||||
@ -130,7 +148,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
${html`<ha-selector
|
${html`<ha-selector
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.selector=${value?.selector ?? { text: undefined }}
|
.selector=${enhancedSelector}
|
||||||
.key=${key}
|
.key=${key}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.required=${value?.default === undefined}
|
.required=${value?.default === undefined}
|
||||||
@ -140,8 +158,10 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
? this.config.use_blueprint.input[key]
|
? this.config.use_blueprint.input[key]
|
||||||
: value?.default}
|
: value?.default}
|
||||||
@value-changed=${this._inputChanged}
|
@value-changed=${this._inputChanged}
|
||||||
|
@item-moved=${this._itemMoved}
|
||||||
></ha-selector>`}
|
></ha-selector>`}
|
||||||
</ha-settings-row>`
|
</ha-settings-row>`;
|
||||||
|
}
|
||||||
)
|
)
|
||||||
: html`<p class="padding">
|
: html`<p class="padding">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -153,6 +173,34 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderReorderModeAlert() {
|
||||||
|
if (!this._reorderMode.active) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-alert
|
||||||
|
class="re-order"
|
||||||
|
alert-type="info"
|
||||||
|
.title=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.title"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.description_all"
|
||||||
|
)}
|
||||||
|
<ha-button slot="action" @click=${this._exitReOrderMode}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.exit"
|
||||||
|
)}
|
||||||
|
</ha-button>
|
||||||
|
</ha-alert>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _exitReOrderMode() {
|
||||||
|
this._reorderMode.exit();
|
||||||
|
}
|
||||||
|
|
||||||
private async _getBlueprints() {
|
private async _getBlueprints() {
|
||||||
this._blueprints = await fetchBlueprints(this.hass, "automation");
|
this._blueprints = await fetchBlueprints(this.hass, "automation");
|
||||||
}
|
}
|
||||||
@ -197,6 +245,29 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _itemMoved(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const { oldIndex, newIndex, oldPath, newPath } = ev.detail;
|
||||||
|
|
||||||
|
const input = nestedArrayMove(
|
||||||
|
this.config.use_blueprint.input,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
oldPath,
|
||||||
|
newPath
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.config,
|
||||||
|
use_blueprint: {
|
||||||
|
...this.config.use_blueprint,
|
||||||
|
input,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async _enable(): Promise<void> {
|
private async _enable(): Promise<void> {
|
||||||
if (!this.hass || !this.stateObj) {
|
if (!this.hass || !this.stateObj) {
|
||||||
return;
|
return;
|
||||||
@ -259,6 +330,10 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
ha-alert.re-order {
|
||||||
|
border-radius: var(--ha-card-border-radius, 12px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import "../../../../components/ha-yaml-editor";
|
|||||||
import type { Condition } from "../../../../data/automation";
|
import type { Condition } from "../../../../data/automation";
|
||||||
import { expandConditionWithShorthand } from "../../../../data/automation";
|
import { expandConditionWithShorthand } from "../../../../data/automation";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant, ItemPath } from "../../../../types";
|
||||||
import "./types/ha-automation-condition-and";
|
import "./types/ha-automation-condition-and";
|
||||||
import "./types/ha-automation-condition-device";
|
import "./types/ha-automation-condition-device";
|
||||||
import "./types/ha-automation-condition-not";
|
import "./types/ha-automation-condition-not";
|
||||||
@ -30,7 +30,7 @@ export default class HaAutomationConditionEditor extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public yamlMode = false;
|
@property({ type: Boolean }) public yamlMode = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@property() public path?: ItemPath;
|
||||||
|
|
||||||
private _processedCondition = memoizeOne((condition) =>
|
private _processedCondition = memoizeOne((condition) =>
|
||||||
expandConditionWithShorthand(condition)
|
expandConditionWithShorthand(condition)
|
||||||
@ -67,8 +67,8 @@ export default class HaAutomationConditionEditor extends LitElement {
|
|||||||
{
|
{
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
condition: condition,
|
condition: condition,
|
||||||
reOrderMode: this.reOrderMode,
|
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
|
path: this.path,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,8 +39,12 @@ import {
|
|||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../../dialogs/generic/show-dialog-box";
|
} from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant, ItemPath } from "../../../../types";
|
||||||
import "./ha-automation-condition-editor";
|
import "./ha-automation-condition-editor";
|
||||||
|
import {
|
||||||
|
ReorderMode,
|
||||||
|
reorderModeContext,
|
||||||
|
} from "../../../../state/reorder-mode-mixin";
|
||||||
|
|
||||||
export interface ConditionElement extends LitElement {
|
export interface ConditionElement extends LitElement {
|
||||||
condition: Condition;
|
condition: Condition;
|
||||||
@ -81,10 +85,10 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public hideMenu = false;
|
@property({ type: Boolean }) public hideMenu = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property() public path?: ItemPath;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "automationClipboard",
|
key: "automationClipboard",
|
||||||
state: false,
|
state: false,
|
||||||
@ -105,10 +109,17 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: reorderModeContext, subscribe: true })
|
||||||
|
private _reorderMode?: ReorderMode;
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.condition) {
|
if (!this.condition) {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const noReorderModeAvailable = this._reorderMode === undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card outlined>
|
<ha-card outlined>
|
||||||
${this.condition.enabled === false
|
${this.condition.enabled === false
|
||||||
@ -163,7 +174,12 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
|
|
||||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
<mwc-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
class=${classMap({ hidden: noReorderModeAvailable })}
|
||||||
|
?aria-hidden=${noReorderModeAvailable}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.re_order"
|
"ui.panel.config.automation.editor.conditions.re_order"
|
||||||
)}
|
)}
|
||||||
@ -297,7 +313,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.condition=${this.condition}
|
.condition=${this.condition}
|
||||||
.reOrderMode=${this.reOrderMode}
|
.path=${this.path}
|
||||||
></ha-automation-condition-editor>
|
></ha-automation-condition-editor>
|
||||||
</div>
|
</div>
|
||||||
</ha-expansion-panel>
|
</ha-expansion-panel>
|
||||||
@ -344,7 +360,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
await this._renameCondition();
|
await this._renameCondition();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
fireEvent(this, "re-order");
|
this._reorderMode?.enter();
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
fireEvent(this, "duplicate");
|
fireEvent(this, "duplicate");
|
||||||
@ -547,6 +563,9 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
mwc-list-item[disabled] {
|
mwc-list-item[disabled] {
|
||||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||||
}
|
}
|
||||||
|
mwc-list-item.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.testing {
|
.testing {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { consume } from "@lit-labs/context";
|
||||||
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
|
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
|
||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
import {
|
import {
|
||||||
@ -8,10 +9,11 @@ import {
|
|||||||
html,
|
html,
|
||||||
nothing,
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
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 { nestedArrayMove } from "../../../../common/util/array-move";
|
||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-button-menu";
|
import "../../../../components/ha-button-menu";
|
||||||
import "../../../../components/ha-sortable";
|
import "../../../../components/ha-sortable";
|
||||||
@ -20,7 +22,11 @@ import type {
|
|||||||
AutomationClipboard,
|
AutomationClipboard,
|
||||||
Condition,
|
Condition,
|
||||||
} from "../../../../data/automation";
|
} from "../../../../data/automation";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import {
|
||||||
|
ReorderMode,
|
||||||
|
reorderModeContext,
|
||||||
|
} from "../../../../state/reorder-mode-mixin";
|
||||||
|
import type { HomeAssistant, ItemPath } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
PASTE_VALUE,
|
PASTE_VALUE,
|
||||||
showAddAutomationElementDialog,
|
showAddAutomationElementDialog,
|
||||||
@ -36,9 +42,11 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public nested = false;
|
@property() public path?: ItemPath;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@state()
|
||||||
|
@consume({ context: reorderModeContext, subscribe: true })
|
||||||
|
private _reorderMode?: ReorderMode;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "automationClipboard",
|
key: "automationClipboard",
|
||||||
@ -89,35 +97,21 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get nested() {
|
||||||
|
return this.path !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!Array.isArray(this.conditions)) {
|
if (!Array.isArray(this.conditions)) {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
${this.reOrderMode && !this.nested
|
|
||||||
? html`
|
|
||||||
<ha-alert
|
|
||||||
alert-type="info"
|
|
||||||
.title=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.re_order_mode.title"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.re_order_mode.description_conditions"
|
|
||||||
)}
|
|
||||||
<ha-button slot="action" @click=${this._exitReOrderMode}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.re_order_mode.exit"
|
|
||||||
)}
|
|
||||||
</ha-button>
|
|
||||||
</ha-alert>
|
|
||||||
`
|
|
||||||
: null}
|
|
||||||
|
|
||||||
<ha-sortable
|
<ha-sortable
|
||||||
handle-selector=".handle"
|
handle-selector=".handle"
|
||||||
.disabled=${!this.reOrderMode}
|
.disabled=${!this._reorderMode?.active}
|
||||||
@item-moved=${this._conditionMoved}
|
@item-moved=${this._conditionMoved}
|
||||||
|
group="conditions"
|
||||||
|
.path=${this.path}
|
||||||
>
|
>
|
||||||
<div class="conditions">
|
<div class="conditions">
|
||||||
${repeat(
|
${repeat(
|
||||||
@ -125,19 +119,18 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
(condition) => this._getKey(condition),
|
(condition) => this._getKey(condition),
|
||||||
(cond, idx) => html`
|
(cond, idx) => html`
|
||||||
<ha-automation-condition-row
|
<ha-automation-condition-row
|
||||||
|
.path=${[...(this.path ?? []), idx]}
|
||||||
.index=${idx}
|
.index=${idx}
|
||||||
.totalConditions=${this.conditions.length}
|
.totalConditions=${this.conditions.length}
|
||||||
.condition=${cond}
|
.condition=${cond}
|
||||||
.hideMenu=${this.reOrderMode}
|
.hideMenu=${Boolean(this._reorderMode?.active)}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@duplicate=${this._duplicateCondition}
|
@duplicate=${this._duplicateCondition}
|
||||||
@move-condition=${this._move}
|
@move-condition=${this._move}
|
||||||
@value-changed=${this._conditionChanged}
|
@value-changed=${this._conditionChanged}
|
||||||
@re-order=${this._enterReOrderMode}
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
>
|
>
|
||||||
${this.reOrderMode
|
${this._reorderMode?.active
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.index=${idx}
|
.index=${idx}
|
||||||
@ -232,16 +225,6 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
fireEvent(this, "value-changed", { value: conditions });
|
fireEvent(this, "value-changed", { value: conditions });
|
||||||
};
|
};
|
||||||
|
|
||||||
private async _enterReOrderMode(ev: CustomEvent) {
|
|
||||||
if (this.nested) return;
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.reOrderMode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _exitReOrderMode() {
|
|
||||||
this.reOrderMode = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getKey(condition: Condition) {
|
private _getKey(condition: Condition) {
|
||||||
if (!this._conditionKeys.has(condition)) {
|
if (!this._conditionKeys.has(condition)) {
|
||||||
this._conditionKeys.set(condition, Math.random().toString());
|
this._conditionKeys.set(condition, Math.random().toString());
|
||||||
@ -262,17 +245,28 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
this._move(index, newIndex);
|
this._move(index, newIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _move(index: number, newIndex: number) {
|
private _move(
|
||||||
const conditions = this.conditions.concat();
|
oldIndex: number,
|
||||||
const condition = conditions.splice(index, 1)[0];
|
newIndex: number,
|
||||||
conditions.splice(newIndex, 0, condition);
|
oldPath?: ItemPath,
|
||||||
|
newPath?: ItemPath
|
||||||
|
) {
|
||||||
|
const conditions = nestedArrayMove(
|
||||||
|
this.conditions,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
oldPath,
|
||||||
|
newPath
|
||||||
|
);
|
||||||
|
|
||||||
fireEvent(this, "value-changed", { value: conditions });
|
fireEvent(this, "value-changed", { value: conditions });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _conditionMoved(ev: CustomEvent): void {
|
private _conditionMoved(ev: CustomEvent): void {
|
||||||
|
if (this.nested) return;
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const { oldIndex, newIndex } = ev.detail;
|
const { oldIndex, newIndex, oldPath, newPath } = ev.detail;
|
||||||
this._move(oldIndex, newIndex);
|
this._move(oldIndex, newIndex, oldPath, newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _conditionChanged(ev: CustomEvent) {
|
private _conditionChanged(ev: CustomEvent) {
|
||||||
|
@ -2,7 +2,7 @@ import { html, LitElement } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
import type { LogicalCondition } from "../../../../../data/automation";
|
import type { LogicalCondition } from "../../../../../data/automation";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant, ItemPath } from "../../../../../types";
|
||||||
import "../ha-automation-condition";
|
import "../ha-automation-condition";
|
||||||
import type { ConditionElement } from "../ha-automation-condition-row";
|
import type { ConditionElement } from "../ha-automation-condition-row";
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ export class HaLogicalCondition extends LitElement implements ConditionElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@property({ attribute: false }) public path?: ItemPath;
|
||||||
|
|
||||||
public static get defaultConfig() {
|
public static get defaultConfig() {
|
||||||
return {
|
return {
|
||||||
@ -25,12 +25,11 @@ export class HaLogicalCondition extends LitElement implements ConditionElement {
|
|||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<ha-automation-condition
|
<ha-automation-condition
|
||||||
nested
|
.path=${[...(this.path ?? []), "conditions"]}
|
||||||
.conditions=${this.condition.conditions || []}
|
.conditions=${this.condition.conditions || []}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.reOrderMode=${this.reOrderMode}
|
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { ensureArray } from "../../../common/array/ensure-array";
|
import { ensureArray } from "../../../common/array/ensure-array";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { nestedArrayMove } from "../../../common/util/array-move";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-markdown";
|
import "../../../components/ha-markdown";
|
||||||
@ -15,6 +16,7 @@ import {
|
|||||||
} from "../../../data/automation";
|
} from "../../../data/automation";
|
||||||
import { Action } from "../../../data/script";
|
import { Action } from "../../../data/script";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
import { ReorderModeMixin } from "../../../state/reorder-mode-mixin";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import "./action/ha-automation-action";
|
import "./action/ha-automation-action";
|
||||||
@ -22,7 +24,7 @@ import "./condition/ha-automation-condition";
|
|||||||
import "./trigger/ha-automation-trigger";
|
import "./trigger/ha-automation-trigger";
|
||||||
|
|
||||||
@customElement("manual-automation-editor")
|
@customElement("manual-automation-editor")
|
||||||
export class HaManualAutomationEditor extends LitElement {
|
export class HaManualAutomationEditor extends ReorderModeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean }) public isWide!: boolean;
|
@property({ type: Boolean }) public isWide!: boolean;
|
||||||
@ -44,7 +46,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
${this.hass.localize("ui.panel.config.automation.editor.migrate")}
|
${this.hass.localize("ui.panel.config.automation.editor.migrate")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</ha-alert>`
|
</ha-alert>`
|
||||||
: ""}
|
: nothing}
|
||||||
${this.stateObj?.state === "off"
|
${this.stateObj?.state === "off"
|
||||||
? html`
|
? html`
|
||||||
<ha-alert alert-type="info">
|
<ha-alert alert-type="info">
|
||||||
@ -92,12 +94,15 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</p>`
|
</p>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
${this._renderReorderModeAlert("triggers")}
|
||||||
|
|
||||||
<ha-automation-trigger
|
<ha-automation-trigger
|
||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="triggers-heading"
|
aria-labelledby="triggers-heading"
|
||||||
.triggers=${this.config.trigger}
|
.triggers=${this.config.trigger}
|
||||||
|
.path=${["trigger"]}
|
||||||
@value-changed=${this._triggerChanged}
|
@value-changed=${this._triggerChanged}
|
||||||
|
@item-moved=${this._itemMoved}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
></ha-automation-trigger>
|
></ha-automation-trigger>
|
||||||
@ -132,12 +137,15 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</p>`
|
</p>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
${this._renderReorderModeAlert("conditions")}
|
||||||
|
|
||||||
<ha-automation-condition
|
<ha-automation-condition
|
||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="conditions-heading"
|
aria-labelledby="conditions-heading"
|
||||||
.conditions=${this.config.condition || []}
|
.conditions=${this.config.condition || []}
|
||||||
|
.path=${["condition"]}
|
||||||
@value-changed=${this._conditionChanged}
|
@value-changed=${this._conditionChanged}
|
||||||
|
@item-moved=${this._itemMoved}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
@ -170,12 +178,15 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</p>`
|
</p>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
${this._renderReorderModeAlert("actions")}
|
||||||
|
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="actions-heading"
|
aria-labelledby="actions-heading"
|
||||||
.actions=${this.config.action}
|
.actions=${this.config.action}
|
||||||
|
.path=${["action"]}
|
||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
|
@item-moved=${this._itemMoved}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@ -183,6 +194,34 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderReorderModeAlert(type: "conditions" | "actions" | "triggers") {
|
||||||
|
if (!this._reorderMode.active) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-alert
|
||||||
|
class="re-order"
|
||||||
|
alert-type="info"
|
||||||
|
.title=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.title"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.re_order_mode.description_${type}`
|
||||||
|
)}
|
||||||
|
<ha-button slot="action" @click=${this._exitReOrderMode}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.exit"
|
||||||
|
)}
|
||||||
|
</ha-button>
|
||||||
|
</ha-alert>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _exitReOrderMode() {
|
||||||
|
this._reorderMode.exit();
|
||||||
|
}
|
||||||
|
|
||||||
private _triggerChanged(ev: CustomEvent): void {
|
private _triggerChanged(ev: CustomEvent): void {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
@ -207,6 +246,21 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _itemMoved(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const { oldIndex, newIndex, oldPath, newPath } = ev.detail;
|
||||||
|
const updatedConfig = nestedArrayMove(
|
||||||
|
this.config,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
oldPath,
|
||||||
|
newPath
|
||||||
|
);
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: updatedConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async _enable(): Promise<void> {
|
private async _enable(): Promise<void> {
|
||||||
if (!this.hass || !this.stateObj) {
|
if (!this.hass || !this.stateObj) {
|
||||||
return;
|
return;
|
||||||
@ -258,6 +312,12 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
}
|
}
|
||||||
|
ha-alert.re-order {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: var(--ha-card-border-radius, 12px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,14 @@ 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 { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
import {
|
||||||
|
CSSResultGroup,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
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 { storage } from "../../../../common/decorators/storage";
|
import { storage } from "../../../../common/decorators/storage";
|
||||||
@ -44,7 +51,7 @@ import {
|
|||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../../dialogs/generic/show-dialog-box";
|
} from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant, ItemPath } from "../../../../types";
|
||||||
import "./types/ha-automation-trigger-calendar";
|
import "./types/ha-automation-trigger-calendar";
|
||||||
import "./types/ha-automation-trigger-device";
|
import "./types/ha-automation-trigger-device";
|
||||||
import "./types/ha-automation-trigger-event";
|
import "./types/ha-automation-trigger-event";
|
||||||
@ -62,6 +69,10 @@ import "./types/ha-automation-trigger-time";
|
|||||||
import "./types/ha-automation-trigger-time_pattern";
|
import "./types/ha-automation-trigger-time_pattern";
|
||||||
import "./types/ha-automation-trigger-webhook";
|
import "./types/ha-automation-trigger-webhook";
|
||||||
import "./types/ha-automation-trigger-zone";
|
import "./types/ha-automation-trigger-zone";
|
||||||
|
import {
|
||||||
|
ReorderMode,
|
||||||
|
reorderModeContext,
|
||||||
|
} from "../../../../state/reorder-mode-mixin";
|
||||||
|
|
||||||
export interface TriggerElement extends LitElement {
|
export interface TriggerElement extends LitElement {
|
||||||
trigger: Trigger;
|
trigger: Trigger;
|
||||||
@ -101,6 +112,8 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property() public path?: ItemPath;
|
||||||
|
|
||||||
@state() private _warnings?: string[];
|
@state() private _warnings?: string[];
|
||||||
|
|
||||||
@state() private _yamlMode = false;
|
@state() private _yamlMode = false;
|
||||||
@ -125,9 +138,17 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entityReg!: EntityRegistryEntry[];
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: reorderModeContext, subscribe: true })
|
||||||
|
private _reorderMode?: ReorderMode;
|
||||||
|
|
||||||
private _triggerUnsub?: Promise<UnsubscribeFunc>;
|
private _triggerUnsub?: Promise<UnsubscribeFunc>;
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
|
if (!this.trigger) return nothing;
|
||||||
|
|
||||||
|
const noReorderModeAvailable = this._reorderMode === undefined;
|
||||||
|
|
||||||
const supported =
|
const supported =
|
||||||
customElements.get(`ha-automation-trigger-${this.trigger.platform}`) !==
|
customElements.get(`ha-automation-trigger-${this.trigger.platform}`) !==
|
||||||
undefined;
|
undefined;
|
||||||
@ -181,7 +202,12 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
|
|
||||||
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
|
<mwc-list-item
|
||||||
|
graphic="icon"
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
class=${classMap({ hidden: noReorderModeAvailable })}
|
||||||
|
?aria-hidden=${noReorderModeAvailable}
|
||||||
|
>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.re_order"
|
"ui.panel.config.automation.editor.triggers.re_order"
|
||||||
)}
|
)}
|
||||||
@ -357,6 +383,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
trigger: this.trigger,
|
trigger: this.trigger,
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
|
path: this.path,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -470,7 +497,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
await this._renameTrigger();
|
await this._renameTrigger();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
fireEvent(this, "re-order");
|
this._reorderMode?.enter();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
this._requestShowId = true;
|
this._requestShowId = true;
|
||||||
@ -702,6 +729,9 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
mwc-list-item[disabled] {
|
mwc-list-item[disabled] {
|
||||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||||
}
|
}
|
||||||
|
mwc-list-item.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
ha-textfield {
|
ha-textfield {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
|
import { consume } from "@lit-labs/context";
|
||||||
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
|
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
|
||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
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 { nestedArrayMove } from "../../../../common/util/array-move";
|
||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-button-menu";
|
import "../../../../components/ha-button-menu";
|
||||||
import "../../../../components/ha-sortable";
|
import "../../../../components/ha-sortable";
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import { AutomationClipboard, Trigger } from "../../../../data/automation";
|
import { AutomationClipboard, Trigger } from "../../../../data/automation";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import {
|
||||||
|
ReorderMode,
|
||||||
|
reorderModeContext,
|
||||||
|
} from "../../../../state/reorder-mode-mixin";
|
||||||
|
import { HomeAssistant, ItemPath } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
PASTE_VALUE,
|
PASTE_VALUE,
|
||||||
showAddAutomationElementDialog,
|
showAddAutomationElementDialog,
|
||||||
@ -26,9 +32,11 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public nested = false;
|
@property() public path?: ItemPath;
|
||||||
|
|
||||||
@property({ type: Boolean }) public reOrderMode = false;
|
@state()
|
||||||
|
@consume({ context: reorderModeContext, subscribe: true })
|
||||||
|
private _reorderMode?: ReorderMode;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "automationClipboard",
|
key: "automationClipboard",
|
||||||
@ -42,31 +50,18 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
|
|
||||||
private _triggerKeys = new WeakMap<Trigger, string>();
|
private _triggerKeys = new WeakMap<Trigger, string>();
|
||||||
|
|
||||||
|
private get nested() {
|
||||||
|
return this.path !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
${this.reOrderMode && !this.nested
|
|
||||||
? html`
|
|
||||||
<ha-alert
|
|
||||||
alert-type="info"
|
|
||||||
.title=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.re_order_mode.title"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.re_order_mode.description_triggers"
|
|
||||||
)}
|
|
||||||
<ha-button slot="action" @click=${this._exitReOrderMode}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.re_order_mode.exit"
|
|
||||||
)}
|
|
||||||
</ha-button>
|
|
||||||
</ha-alert>
|
|
||||||
`
|
|
||||||
: null}
|
|
||||||
<ha-sortable
|
<ha-sortable
|
||||||
handle-selector=".handle"
|
handle-selector=".handle"
|
||||||
.disabled=${!this.reOrderMode}
|
.disabled=${!this._reorderMode?.active}
|
||||||
@item-moved=${this._triggerMoved}
|
@item-moved=${this._triggerMoved}
|
||||||
|
group="triggers"
|
||||||
|
.path=${this.path}
|
||||||
>
|
>
|
||||||
<div class="triggers">
|
<div class="triggers">
|
||||||
${repeat(
|
${repeat(
|
||||||
@ -74,16 +69,16 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
(trigger) => this._getKey(trigger),
|
(trigger) => this._getKey(trigger),
|
||||||
(trg, idx) => html`
|
(trg, idx) => html`
|
||||||
<ha-automation-trigger-row
|
<ha-automation-trigger-row
|
||||||
|
.path=${[...(this.path ?? []), idx]}
|
||||||
.index=${idx}
|
.index=${idx}
|
||||||
.trigger=${trg}
|
.trigger=${trg}
|
||||||
.hideMenu=${this.reOrderMode}
|
.hideMenu=${Boolean(this._reorderMode?.active)}
|
||||||
@duplicate=${this._duplicateTrigger}
|
@duplicate=${this._duplicateTrigger}
|
||||||
@value-changed=${this._triggerChanged}
|
@value-changed=${this._triggerChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@re-order=${this._enterReOrderMode}
|
|
||||||
>
|
>
|
||||||
${this.reOrderMode
|
${this._reorderMode?.active
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.index=${idx}
|
.index=${idx}
|
||||||
@ -173,16 +168,6 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _enterReOrderMode(ev: CustomEvent) {
|
|
||||||
if (this.nested) return;
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.reOrderMode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _exitReOrderMode() {
|
|
||||||
this.reOrderMode = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getKey(action: Trigger) {
|
private _getKey(action: Trigger) {
|
||||||
if (!this._triggerKeys.has(action)) {
|
if (!this._triggerKeys.has(action)) {
|
||||||
this._triggerKeys.set(action, Math.random().toString());
|
this._triggerKeys.set(action, Math.random().toString());
|
||||||
@ -203,17 +188,28 @@ export default class HaAutomationTrigger extends LitElement {
|
|||||||
this._move(index, newIndex);
|
this._move(index, newIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _move(index: number, newIndex: number) {
|
private _move(
|
||||||
const triggers = this.triggers.concat();
|
oldIndex: number,
|
||||||
const trigger = triggers.splice(index, 1)[0];
|
newIndex: number,
|
||||||
triggers.splice(newIndex, 0, trigger);
|
oldPath?: ItemPath,
|
||||||
|
newPath?: ItemPath
|
||||||
|
) {
|
||||||
|
const triggers = nestedArrayMove(
|
||||||
|
this.triggers,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
oldPath,
|
||||||
|
newPath
|
||||||
|
);
|
||||||
|
|
||||||
fireEvent(this, "value-changed", { value: triggers });
|
fireEvent(this, "value-changed", { value: triggers });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _triggerMoved(ev: CustomEvent): void {
|
private _triggerMoved(ev: CustomEvent): void {
|
||||||
|
if (this.nested) return;
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const { oldIndex, newIndex } = ev.detail;
|
const { oldIndex, newIndex, oldPath, newPath } = ev.detail;
|
||||||
this._move(oldIndex, newIndex);
|
this._move(oldIndex, newIndex, oldPath, newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _triggerChanged(ev: CustomEvent) {
|
private _triggerChanged(ev: CustomEvent) {
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { nestedArrayMove } from "../../../common/util/array-move";
|
||||||
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-blueprint-picker";
|
import "../../../components/ha-blueprint-picker";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-circular-progress";
|
import "../../../components/ha-circular-progress";
|
||||||
import "../../../components/ha-markdown";
|
import "../../../components/ha-markdown";
|
||||||
import "../../../components/ha-selector/ha-selector";
|
import "../../../components/ha-selector/ha-selector";
|
||||||
import "../../../components/ha-settings-row";
|
import "../../../components/ha-settings-row";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BlueprintOrError,
|
BlueprintOrError,
|
||||||
Blueprints,
|
Blueprints,
|
||||||
@ -17,9 +18,10 @@ import { BlueprintScriptConfig } from "../../../data/script";
|
|||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import "../ha-config-section";
|
import "../ha-config-section";
|
||||||
|
import { ReorderModeMixin } from "../../../state/reorder-mode-mixin";
|
||||||
|
|
||||||
@customElement("blueprint-script-editor")
|
@customElement("blueprint-script-editor")
|
||||||
export class HaBlueprintScriptEditor extends LitElement {
|
export class HaBlueprintScriptEditor extends ReorderModeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean }) public isWide!: boolean;
|
@property({ type: Boolean }) public isWide!: boolean;
|
||||||
@ -55,6 +57,7 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
</mwc-button>
|
</mwc-button>
|
||||||
</ha-alert>`
|
</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
|
${this._renderReorderModeAlert()}
|
||||||
<ha-card
|
<ha-card
|
||||||
outlined
|
outlined
|
||||||
class="blueprint"
|
class="blueprint"
|
||||||
@ -82,7 +85,6 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
)
|
)
|
||||||
: html`<ha-circular-progress indeterminate></ha-circular-progress>`}
|
: html`<ha-circular-progress indeterminate></ha-circular-progress>`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${this.config.use_blueprint.path
|
${this.config.use_blueprint.path
|
||||||
? blueprint && "error" in blueprint
|
? blueprint && "error" in blueprint
|
||||||
? html`<p class="warning padding">
|
? html`<p class="warning padding">
|
||||||
@ -98,8 +100,23 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
${blueprint?.metadata?.input &&
|
${blueprint?.metadata?.input &&
|
||||||
Object.keys(blueprint.metadata.input).length
|
Object.keys(blueprint.metadata.input).length
|
||||||
? Object.entries(blueprint.metadata.input).map(
|
? Object.entries(blueprint.metadata.input).map(
|
||||||
([key, value]) =>
|
([key, value]) => {
|
||||||
html`<ha-settings-row .narrow=${this.narrow}>
|
const selector = value?.selector ?? { text: undefined };
|
||||||
|
const type = Object.keys(selector)[0];
|
||||||
|
const enhancedSelector = [
|
||||||
|
"action",
|
||||||
|
"condition",
|
||||||
|
"trigger",
|
||||||
|
].includes(type)
|
||||||
|
? {
|
||||||
|
[type]: {
|
||||||
|
...selector[type],
|
||||||
|
path: [key],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: selector;
|
||||||
|
|
||||||
|
return html`<ha-settings-row .narrow=${this.narrow}>
|
||||||
<span slot="heading">${value?.name || key}</span>
|
<span slot="heading">${value?.name || key}</span>
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
slot="description"
|
slot="description"
|
||||||
@ -109,7 +126,7 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
${html`<ha-selector
|
${html`<ha-selector
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.selector=${value?.selector ?? { text: undefined }}
|
.selector=${enhancedSelector}
|
||||||
.key=${key}
|
.key=${key}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.required=${value?.default === undefined}
|
.required=${value?.default === undefined}
|
||||||
@ -119,8 +136,10 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
? this.config.use_blueprint.input[key]
|
? this.config.use_blueprint.input[key]
|
||||||
: value?.default}
|
: value?.default}
|
||||||
@value-changed=${this._inputChanged}
|
@value-changed=${this._inputChanged}
|
||||||
|
@item-moved=${this._itemMoved}
|
||||||
></ha-selector>`}
|
></ha-selector>`}
|
||||||
</ha-settings-row>`
|
</ha-settings-row>`;
|
||||||
|
}
|
||||||
)
|
)
|
||||||
: html`<p class="padding">
|
: html`<p class="padding">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -132,6 +151,34 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderReorderModeAlert() {
|
||||||
|
if (!this._reorderMode.active) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-alert
|
||||||
|
class="re-order"
|
||||||
|
alert-type="info"
|
||||||
|
.title=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.title"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.description_all"
|
||||||
|
)}
|
||||||
|
<ha-button slot="action" @click=${this._exitReOrderMode}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.exit"
|
||||||
|
)}
|
||||||
|
</ha-button>
|
||||||
|
</ha-alert>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _exitReOrderMode() {
|
||||||
|
this._reorderMode.exit();
|
||||||
|
}
|
||||||
|
|
||||||
private async _getBlueprints() {
|
private async _getBlueprints() {
|
||||||
this._blueprints = await fetchBlueprints(this.hass, "script");
|
this._blueprints = await fetchBlueprints(this.hass, "script");
|
||||||
}
|
}
|
||||||
@ -176,6 +223,29 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _itemMoved(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const { oldIndex, newIndex, oldPath, newPath } = ev.detail;
|
||||||
|
|
||||||
|
const input = nestedArrayMove(
|
||||||
|
this.config.use_blueprint.input,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
oldPath,
|
||||||
|
newPath
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.config,
|
||||||
|
use_blueprint: {
|
||||||
|
...this.config.use_blueprint,
|
||||||
|
input,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _duplicate() {
|
private _duplicate() {
|
||||||
fireEvent(this, "duplicate");
|
fireEvent(this, "duplicate");
|
||||||
}
|
}
|
||||||
@ -229,6 +299,10 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
ha-alert.re-order {
|
||||||
|
border-radius: var(--ha-card-border-radius, 12px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,12 @@ import { mdiHelpCircle } from "@mdi/js";
|
|||||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { nestedArrayMove } from "../../../common/util/array-move";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import { Action, Fields, ScriptConfig } from "../../../data/script";
|
import { Action, Fields, ScriptConfig } from "../../../data/script";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
import { ReorderModeMixin } from "../../../state/reorder-mode-mixin";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import "../automation/action/ha-automation-action";
|
import "../automation/action/ha-automation-action";
|
||||||
@ -14,7 +16,7 @@ import "./ha-script-fields";
|
|||||||
import type HaScriptFields from "./ha-script-fields";
|
import type HaScriptFields from "./ha-script-fields";
|
||||||
|
|
||||||
@customElement("manual-script-editor")
|
@customElement("manual-script-editor")
|
||||||
export class HaManualScriptEditor extends LitElement {
|
export class HaManualScriptEditor extends ReorderModeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean }) public isWide!: boolean;
|
@property({ type: Boolean }) public isWide!: boolean;
|
||||||
@ -118,11 +120,15 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
${this._renderReorderModeAlert()}
|
||||||
|
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="sequence-heading"
|
aria-labelledby="sequence-heading"
|
||||||
.actions=${this.config.sequence}
|
.actions=${this.config.sequence}
|
||||||
|
.path=${["sequence"]}
|
||||||
@value-changed=${this._sequenceChanged}
|
@value-changed=${this._sequenceChanged}
|
||||||
|
@item-moved=${this._itemMoved}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@ -130,6 +136,34 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderReorderModeAlert() {
|
||||||
|
if (!this._reorderMode.active) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-alert
|
||||||
|
class="re-order"
|
||||||
|
alert-type="info"
|
||||||
|
.title=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.title"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.description_all"
|
||||||
|
)}
|
||||||
|
<ha-button slot="action" @click=${this._exitReOrderMode}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.re_order_mode.exit"
|
||||||
|
)}
|
||||||
|
</ha-button>
|
||||||
|
</ha-alert>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _exitReOrderMode() {
|
||||||
|
this._reorderMode.exit();
|
||||||
|
}
|
||||||
|
|
||||||
private _fieldsChanged(ev: CustomEvent): void {
|
private _fieldsChanged(ev: CustomEvent): void {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
@ -144,6 +178,21 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _itemMoved(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const { oldIndex, newIndex, oldPath, newPath } = ev.detail;
|
||||||
|
const updatedConfig = nestedArrayMove(
|
||||||
|
this.config,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
oldPath,
|
||||||
|
newPath
|
||||||
|
);
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: updatedConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _duplicate() {
|
private _duplicate() {
|
||||||
fireEvent(this, "duplicate");
|
fireEvent(this, "duplicate");
|
||||||
}
|
}
|
||||||
@ -179,6 +228,12 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
.header a {
|
.header a {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
ha-alert.re-order {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: var(--ha-card-border-radius, 12px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
40
src/state/reorder-mode-mixin.ts
Normal file
40
src/state/reorder-mode-mixin.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { ContextProvider, createContext } from "@lit-labs/context";
|
||||||
|
import { LitElement } from "lit";
|
||||||
|
import { Constructor } from "../types";
|
||||||
|
|
||||||
|
export type ReorderMode = {
|
||||||
|
active: boolean;
|
||||||
|
enter: () => void;
|
||||||
|
exit: () => void;
|
||||||
|
};
|
||||||
|
export const reorderModeContext = createContext<ReorderMode>("reorder-mode");
|
||||||
|
|
||||||
|
export const ReorderModeMixin = <T extends Constructor<LitElement>>(
|
||||||
|
superClass: T
|
||||||
|
) =>
|
||||||
|
class extends superClass {
|
||||||
|
private _reorderModeProvider = new ContextProvider(this, {
|
||||||
|
context: reorderModeContext,
|
||||||
|
initialValue: {
|
||||||
|
active: false,
|
||||||
|
enter: () => {
|
||||||
|
this._reorderModeProvider.setValue({
|
||||||
|
...this._reorderModeProvider.value,
|
||||||
|
active: true,
|
||||||
|
});
|
||||||
|
this.requestUpdate("_reorderMode");
|
||||||
|
},
|
||||||
|
exit: () => {
|
||||||
|
this._reorderModeProvider.setValue({
|
||||||
|
...this._reorderModeProvider.value,
|
||||||
|
active: false,
|
||||||
|
});
|
||||||
|
this.requestUpdate("_reorderMode");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
get _reorderMode() {
|
||||||
|
return this._reorderModeProvider.value;
|
||||||
|
}
|
||||||
|
};
|
@ -2444,6 +2444,7 @@
|
|||||||
"description_triggers": "You are in re-order mode, you can re-order your triggers.",
|
"description_triggers": "You are in re-order mode, you can re-order your triggers.",
|
||||||
"description_conditions": "You are in re-order mode, you can re-order your conditions.",
|
"description_conditions": "You are in re-order mode, you can re-order your conditions.",
|
||||||
"description_actions": "You are in re-order mode, you can re-order your actions.",
|
"description_actions": "You are in re-order mode, you can re-order your actions.",
|
||||||
|
"description_all": "You are in re-order mode, you can re-order your triggers, conditions and actions.",
|
||||||
"exit": "Exit"
|
"exit": "Exit"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
|
@ -294,3 +294,5 @@ export type AsyncReturnType<T extends (...args: any) => any> = T extends (
|
|||||||
: never;
|
: never;
|
||||||
|
|
||||||
export type Entries<T> = [keyof T, T[keyof T]][];
|
export type Entries<T> = [keyof T, T[keyof T]][];
|
||||||
|
|
||||||
|
export type ItemPath = (number | string)[];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user