@@ -132,6 +199,15 @@ export default class HaAutomationAction extends LitElement {
`;
}
+ private _actionClicked(ev: MouseEvent) {
+ fireEvent(this, "element-selected", {
+ type: "action",
+ element: (ev.currentTarget as HaAutomationActionRow).action,
+ index: (ev.currentTarget as HaAutomationActionRow).index,
+ path: (ev.currentTarget as HaAutomationActionRow).path,
+ });
+ }
+
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts
index 5fe57a14e5..e2fb1d620f 100644
--- a/src/panels/config/automation/condition/ha-automation-condition-row.ts
+++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts
@@ -128,7 +128,7 @@ export default class HaAutomationConditionRow extends LitElement {
`
: ""}
-
+
-
- ${this.hass.localize(
- `ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
- )}
-
-
-
`
: ""}
-
-
+
ha-alert {
margin: 0 auto;
@@ -1100,6 +1099,12 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
padding: 28px 20px 0;
display: block;
}
+ manual-automation-editor {
+ margin: 0 auto;
+ max-width: 1540px;
+ padding: 28px 20px 0;
+ display: block;
+ }
ha-yaml-editor {
flex-grow: 1;
--actions-border-radius: 0;
diff --git a/src/panels/config/automation/manual-automation-editor.ts b/src/panels/config/automation/manual-automation-editor.ts
index 3d69ee8752..60f8638afd 100644
--- a/src/panels/config/automation/manual-automation-editor.ts
+++ b/src/panels/config/automation/manual-automation-editor.ts
@@ -1,5 +1,11 @@
import "@material/mwc-button/mwc-button";
-import { mdiHelpCircle } from "@mdi/js";
+import {
+ mdiClose,
+ mdiDotsVertical,
+ mdiHelpCircle,
+ mdiIdentifier,
+ mdiPlaylistEdit,
+} from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import type { CSSResultGroup, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
@@ -49,6 +55,51 @@ import { constructUrlCurrentPath } from "../../../common/url/construct-url";
import { canOverrideAlphanumericInput } from "../../../common/dom/can-override-input";
import { showToast } from "../../../util/toast";
import { showPasteReplaceDialog } from "./paste-replace-dialog/show-dialog-paste-replace";
+import "@shoelace-style/shoelace/dist/components/split-panel/split-panel";
+import "@shoelace-style/shoelace/dist/components/drawer/drawer";
+import { dynamicElement } from "../../../common/dom/dynamic-element-directive";
+import { classMap } from "lit/directives/class-map";
+import { getType } from "./action/ha-automation-action-row";
+import { storage } from "../../../common/decorators/storage";
+import { nextRender } from "../../../common/util/render-status";
+import {
+ DIRECTION_ALL,
+ DIRECTION_VERTICAL,
+ Manager,
+ Pan,
+ Swipe,
+} from "@egjs/hammerjs";
+
+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);
+}
+
+function updateNestedItem(obj: any, path: ItemPath, newValue): any {
+ const lastKey = path.pop()!;
+ const parent = findNestedItem(obj, path);
+ parent[lastKey] = newValue
+ ? newValue
+ : Array.isArray(parent[lastKey])
+ ? [...parent[lastKey]]
+ : [parent[lastKey]];
+ return obj;
+}
const baseConfigStruct = object({
alias: optional(string()),
@@ -85,6 +136,14 @@ export class HaManualAutomationEditor extends LitElement {
@state() private _pastedConfig?: ManualAutomationConfig;
+ @state() private _selectedElement?: any;
+
+ @state()
+ @storage({ key: "automationSidebarPosition" })
+ private _sidebarWidth = 99999;
+
+ @state() private _yamlMode = false;
+
private _previousConfig?: ManualAutomationConfig;
public connectedCallback() {
@@ -114,6 +173,13 @@ export class HaManualAutomationEditor extends LitElement {
}
}
+ protected updated(changedProps: PropertyValues): void {
+ super.updated(changedProps);
+ if (changedProps.has("narrow") && this.narrow && this._selectedElement) {
+ this.renderRoot.querySelector("sl-drawer").show();
+ }
+ }
+
private _clearParam(param: string) {
window.history.replaceState(
null,
@@ -123,151 +189,411 @@ export class HaManualAutomationEditor extends LitElement {
}
protected render() {
- return html`
- ${this.stateObj?.state === "off"
- ? html`
-
- ${this.hass.localize(
- "ui.panel.config.automation.editor.disabled"
- )}
-
- ${this.hass.localize(
- "ui.panel.config.automation.editor.enable"
- )}
-
-
- `
- : nothing}
- ${this.config.description
- ? html`
`
- : nothing}
-
- ${!ensureArray(this.config.triggers)?.length
- ? html`
- ${this.hass.localize(
- "ui.panel.config.automation.editor.triggers.description"
- )}
-
`
- : nothing}
+ const selectedElement = this._selectedElement?.element;
+ const selectedElementType = this._selectedElement?.type;
+ const path = this._selectedElement?.path || [];
-
+ const type = "";
+ const supported = true;
+ const yamlMode = this._yamlMode;
-
- ${!ensureArray(this.config.conditions)?.length
- ? html`
- ${this.hass.localize(
- "ui.panel.config.automation.editor.conditions.description",
- { user: this.hass.user?.name || "Alice" }
- )}
-
`
- : nothing}
-
-
-
-
- ${!ensureArray(this.config.actions)?.length
- ? html`
- ${this.hass.localize(
- "ui.panel.config.automation.editor.actions.description"
- )}
-
`
- : nothing}
+
${`Edit ${selectedElementType}`}
+
+
+ ${selectedElementType === "trigger"
+ ? html`
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.triggers.edit_id"
+ )}
+
+ `
+ : nothing}
+
+ ${this.hass.localize(
+ `ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
+ )}
+
+
+
+
+
+ ${this._yamlMode
+ ? html`
`
+ : selectedElementType === "trigger"
+ ? html`
+ ${dynamicElement(
+ `ha-automation-trigger-${selectedElement.trigger}`,
+ {
+ hass: this.hass,
+ trigger: selectedElement,
+ disabled: this.disabled,
+ }
+ )}
+
`
+ : selectedElementType === "condition"
+ ? html`
`
+ : selectedElementType === "action"
+ ? html`
+ ${dynamicElement(
+ `ha-automation-action-${getType(selectedElement)}`,
+ {
+ hass: this.hass,
+ action: selectedElement,
+ narrow: true,
+ disabled: this.disabled,
+ }
+ )}
+
`
+ : nothing}
+
`
+ : nothing;
-
+ return html`
+ ${this.narrow
+ ? html`
+ ${sidePanel}
+ `
+ : nothing}
+
+
+ ${this.stateObj?.state === "off"
+ ? html`
+
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.disabled"
+ )}
+
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.enable"
+ )}
+
+
+ `
+ : nothing}
+ ${this.config.description
+ ? html`
`
+ : nothing}
+
+ ${!ensureArray(this.config.triggers)?.length
+ ? html`
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.triggers.description"
+ )}
+
`
+ : nothing}
+
+
+
+
+ ${!ensureArray(this.config.conditions)?.length
+ ? html`
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.conditions.description",
+ { user: this.hass.user?.name || "Alice" }
+ )}
+
`
+ : nothing}
+
+
+
+
+ ${!ensureArray(this.config.actions)?.length
+ ? html`
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.actions.description"
+ )}
+
`
+ : nothing}
+
+
+
+ ${!this.narrow && selectedElement
+ ? html`
+ ${sidePanel}
+ `
+ : nothing}
+
`;
}
+ private _onUiChanged(ev: CustomEvent): void {
+ ev.stopPropagation();
+ const path = ev.currentTarget?.path || [];
+
+ const newConfig = updateNestedItem(
+ { ...this.config },
+ path,
+ ev.detail.value
+ );
+
+ console.log(newConfig);
+
+ fireEvent(this, "value-changed", { value: newConfig });
+ }
+
+ private async _toggleYamlMode() {
+ this._yamlMode = !this._yamlMode;
+ if (this._yamlMode) {
+ await this.updateComplete;
+ // this.renderRoot.querySelector("ha-yaml-editor").positionInPixels = 0;
+ }
+ }
+
+ private async _elementSelected(ev) {
+ console.log(ev);
+ this._selectedElement = ev.detail;
+ console.log("repo", this._sidebarWidth);
+ const target = ev.target;
+ await this.updateComplete;
+ this.renderRoot.querySelector("sl-split-panel").positionInPixels =
+ this.clientWidth - 40 - this._sidebarWidth;
+ if (this.narrow) {
+ this.renderRoot.querySelector("sl-drawer").show();
+ console.log(target);
+ this._targetEl = target;
+ }
+ }
+
+ private _splitPanelRepositioned(ev: CustomEvent): void {
+ if (!this._selectedElement) {
+ return;
+ }
+ console.log(ev);
+ console.log("reposition", ev.target.positionInPixels);
+ let sidebarWidth = ev.target.clientWidth - ev.target.positionInPixels;
+ if (this._oldClientWidth && this._oldClientWidth !== this.clientWidth) {
+ // If the client width has changed, we need to subtract the difference
+ sidebarWidth = sidebarWidth + (this._oldClientWidth - this.clientWidth);
+ }
+ this._oldClientWidth = this.clientWidth;
+ console.log(sidebarWidth);
+ console.log(this.clientWidth);
+ console.log(this.clientWidth - 40 - sidebarWidth);
+ // if (Math.abs(sidebarWidth - this._sidebarWidth) > 20) {
+ // this._sidebarWidth = sidebarWidth;
+ // }
+ this._sidebarWidth = sidebarWidth;
+ }
+
+ private _closeSidebar() {
+ if (this.narrow) {
+ this.renderRoot.querySelector("sl-drawer").hide();
+ }
+ this._selectedElement = undefined;
+ }
+
+ private async _drawerOpen() {
+ // this._oldScrollPosition = window.scrollY;
+ this.renderRoot.querySelector("div[slot='start']").style.paddingBottom =
+ "66vh";
+ await nextRender();
+ fireEvent(this, "scroll-to", {
+ up: this._targetEl.getBoundingClientRect().top,
+ });
+ this._setupListeners();
+ }
+
+ private _setupListeners() {
+ const mc = new Manager(this.renderRoot.querySelector("ha-dialog-header"), {
+ touchAction: "pan-y",
+ });
+
+ mc.add(
+ new Swipe({
+ direction: DIRECTION_VERTICAL,
+ })
+ );
+ mc.on("swipeup", (e) => {
+ console.log("up", e);
+ this.toggleAttribute("big-drawer", true);
+ });
+
+ mc.on("swipedown", (e) => {
+ console.log("down", e);
+ if (this.hasAttribute("big-drawer")) {
+ this.toggleAttribute("big-drawer", false);
+ } else {
+ this.renderRoot.querySelector("sl-drawer").hide();
+ }
+ });
+
+ this._manager = mc;
+ }
+
+ private _drawerClose() {
+ this.renderRoot.querySelector("div[slot='start']").style.paddingBottom =
+ "0";
+ }
+
private _triggerChanged(ev: CustomEvent): void {
ev.stopPropagation();
this.resetPastedConfig();
@@ -552,6 +878,45 @@ export class HaManualAutomationEditor extends LitElement {
font-weight: var(--ha-font-weight-normal);
line-height: 0;
}
+
+ sl-split-panel {
+ height: calc(100vh - var(--header-height, 64px) - 28px - 20px - 1px);
+ }
+
+ sl-drawer {
+ --sl-z-index-drawer: 9999;
+ --size: 66vh;
+ --sl-panel-background-color: var(--ha-card-background, white);
+ --sl-overlay-background-color: rgba(0, 0, 0, 0.32);
+ --sl-shadow-x-large: var(
+ --ha-card-box-shadow,
+ 0px -1px 4px 1px rgba(0, 0, 0, 0.2),
+ 0px 1px 1px 0px rgba(0, 0, 0, 0.14),
+ 0px 1px 3px 0px rgba(0, 0, 0, 0.12)
+ );
+ --sl-panel-border-color: var(--ha-card-border-color, #e0e0e0);
+ }
+ :host([big-drawer]) sl-drawer {
+ --size: 90vh;
+ }
+ sl-drawer::part(panel) {
+ border-radius: 12px 12px 0 0;
+ border: 1px solid var(--ha-card-border-color, #e0e0e0);
+ }
+ sl-drawer .card-content {
+ padding: 12px;
+ }
+ sl-drawer ha-dialog-header {
+ position: sticky;
+ top: 0;
+ background: var(--card-background-color);
+ z-index: 999;
+ }
+ .card-content {
+ overflow: auto;
+ height: 100%;
+ padding-bottom: 16px;
+ }
`,
];
}
diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts
index f9f4c28ec2..c7caa764dc 100644
--- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts
+++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts
@@ -70,6 +70,7 @@ import "./types/ha-automation-trigger-time";
import "./types/ha-automation-trigger-time_pattern";
import "./types/ha-automation-trigger-webhook";
import "./types/ha-automation-trigger-zone";
+import "../../../../components/ha-automation-row";
export interface TriggerElement extends LitElement {
trigger: Trigger;
@@ -158,7 +159,7 @@ export default class HaAutomationTriggerRow extends LitElement {
`
: nothing}
-
+
-
- ${this.hass.localize(
- "ui.panel.config.automation.editor.triggers.edit_id"
- )}
-
-
-
-
- ${this.hass.localize(
- `ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
- )}
-
-
-
-
-
- ${this._warnings
- ? html`
- ${this._warnings.length && this._warnings[0] !== undefined
- ? html`
- ${this._warnings.map(
- (warning) => html`- ${warning}
`
- )}
-
`
- : ""}
- ${this.hass.localize(
- "ui.errors.config.edit_in_yaml_supported"
- )}
- `
- : ""}
- ${yamlMode
- ? html`
- ${!supported
- ? html`
- ${this.hass.localize(
- "ui.panel.config.automation.editor.triggers.unsupported_platform",
- { platform: type }
- )}
- `
- : ""}
-
- `
- : html`
- ${showId && !isTriggerList(this.trigger)
- ? html`
-
-
- `
- : ""}
-
- ${dynamicElement(`ha-automation-trigger-${type}`, {
- hass: this.hass,
- trigger: this.trigger,
- disabled: this.disabled,
- })}
-
- `}
-
-
+
{
let triggers: Trigger[];
if (value === PASTE_VALUE) {
diff --git a/src/panels/config/integrations/integration-panels/zha/zha-device-card.ts b/src/panels/config/integrations/integration-panels/zha/zha-device-card.ts
index 4e0908e746..1c94eb18ca 100644
--- a/src/panels/config/integrations/integration-panels/zha/zha-device-card.ts
+++ b/src/panels/config/integrations/integration-panels/zha/zha-device-card.ts
@@ -156,7 +156,7 @@ class ZHADeviceCard extends SubscribeMixin(LitElement) {
newName = name.replace(oldDeviceName, newDeviceName);
}
- if (newName !== undefined && !newEntityId) {
+ if (newName === undefined && !newEntityId) {
return undefined;
}
diff --git a/src/panels/config/script/ha-script-field-row.ts b/src/panels/config/script/ha-script-field-row.ts
index 7c3d68fd23..7dbc207f25 100644
--- a/src/panels/config/script/ha-script-field-row.ts
+++ b/src/panels/config/script/ha-script-field-row.ts
@@ -355,8 +355,8 @@ export default class HaScriptFieldRow extends LitElement {
}
:host([highlight]) ha-card {
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
- --shadow-focus: 0 0 0 1px var(--state-inactive-color);
- border-color: var(--state-inactive-color);
+ --shadow-focus: 0 0 0 1px var(--primary-color);
+ border-color: var(--primary-color);
box-shadow: var(--shadow-default), var(--shadow-focus);
}
`,