diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index ebd10a0cd4..a5b93a7d67 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -1154,6 +1154,12 @@ export class HaAutomationEditor extends PreventUnsavedMixin( return [ haStyle, css` + :host { + --ha-automation-editor-max-width: var( + --ha-automation-editor-width, + 1540px + ); + } ha-fade-in { display: flex; justify-content: center; @@ -1175,7 +1181,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin( } manual-automation-editor { - max-width: 1540px; + max-width: var(--ha-automation-editor-max-width); padding: 0 12px; } diff --git a/src/panels/config/automation/ha-automation-sidebar.ts b/src/panels/config/automation/ha-automation-sidebar.ts index 4a51370660..bdb4e1ed50 100644 --- a/src/panels/config/automation/ha-automation-sidebar.ts +++ b/src/panels/config/automation/ha-automation-sidebar.ts @@ -1,5 +1,7 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { computeRTL } from "../../../common/util/compute_rtl"; import "../../../components/ha-resizable-bottom-sheet"; import type { HaResizableBottomSheet } from "../../../components/ha-resizable-bottom-sheet"; import { @@ -37,9 +39,18 @@ export default class HaAutomationSidebar extends LitElement { @state() private _yamlMode = false; + @state() private _resizing = false; + @query("ha-resizable-bottom-sheet") private _bottomSheetElement?: HaResizableBottomSheet; + private _resizeStartX = 0; + + disconnectedCallback() { + super.disconnectedCallback(); + this._unregisterResizeHandlers(); + } + private _renderContent() { // get config type const type = this._getType(); @@ -154,7 +165,16 @@ export default class HaAutomationSidebar extends LitElement { `; } - return this._renderContent(); + return html` +
+ ${this._resizing ? html`
` : nothing} +
+ ${this._renderContent()} + `; } private _getType() { @@ -207,6 +227,67 @@ export default class HaAutomationSidebar extends LitElement { (this.config as ActionSidebarConfig)?.toggleYamlMode(); }; + private _handleMouseDown = (ev: MouseEvent | TouchEvent) => { + // Prevent the browser from interpreting this as a scroll/PTR gesture. + ev.preventDefault(); + this._startResizing( + (ev as TouchEvent).touches?.length + ? (ev as TouchEvent).touches[0].clientX + : (ev as MouseEvent).clientX + ); + }; + + private _startResizing(clientX: number) { + // register event listeners for drag handling + document.addEventListener("mousemove", this._handleMouseMove); + document.addEventListener("mouseup", this._endResizing); + document.addEventListener("touchmove", this._handleMouseMove, { + passive: false, + }); + document.addEventListener("touchend", this._endResizing); + document.addEventListener("touchcancel", this._endResizing); + + this._resizing = true; + this._resizeStartX = clientX; + } + + private _handleMouseMove = (ev: MouseEvent | TouchEvent) => { + this._updateSize( + (ev as TouchEvent).touches?.length + ? (ev as TouchEvent).touches[0].clientX + : (ev as MouseEvent).clientX + ); + }; + + private _updateSize(clientX: number) { + let delta = this._resizeStartX - clientX; + + if (computeRTL(this.hass)) { + delta = -delta; + } + + requestAnimationFrame(() => { + fireEvent(this, "sidebar-resized", { + deltaInPx: delta, + }); + }); + } + + private _endResizing = () => { + this._unregisterResizeHandlers(); + this._resizing = false; + document.body.style.removeProperty("cursor"); + fireEvent(this, "sidebar-resizing-stopped"); + }; + + private _unregisterResizeHandlers() { + document.removeEventListener("mousemove", this._handleMouseMove); + document.removeEventListener("mouseup", this._endResizing); + document.removeEventListener("touchmove", this._handleMouseMove); + document.removeEventListener("touchend", this._endResizing); + document.removeEventListener("touchcancel", this._endResizing); + } + static styles = css` :host { z-index: 6; @@ -231,6 +312,28 @@ export default class HaAutomationSidebar extends LitElement { max-height: 100%; } } + + .handle { + position: absolute; + margin-inline-start: -11px; + height: calc(100% - (2 * var(--ha-card-border-radius))); + width: 24px; + z-index: 7; + cursor: ew-resize; + display: flex; + align-items: center; + justify-content: center; + padding: var(--ha-card-border-radius) 0; + } + .handle.resizing { + cursor: grabbing; + } + .handle .indicator { + background-color: var(--primary-color); + height: 100%; + width: 4px; + border-radius: var(--ha-border-radius-pill); + } `; } @@ -244,5 +347,9 @@ declare global { "yaml-changed": { value: unknown; }; + "sidebar-resized": { + deltaInPx: number; + }; + "sidebar-resizing-stopped": undefined; } } diff --git a/src/panels/config/automation/manual-automation-editor.ts b/src/panels/config/automation/manual-automation-editor.ts index 55b9a52cbf..724675da61 100644 --- a/src/panels/config/automation/manual-automation-editor.ts +++ b/src/panels/config/automation/manual-automation-editor.ts @@ -22,6 +22,7 @@ import { union, } from "superstruct"; import { ensureArray } from "../../../common/array/ensure-array"; +import { storage } from "../../../common/decorators/storage"; import { canOverrideAlphanumericInput } from "../../../common/dom/can-override-input"; import { fireEvent } from "../../../common/dom/fire_event"; import { constructUrlCurrentPath } from "../../../common/url/construct-url"; @@ -77,6 +78,8 @@ const automationConfigStruct = union([ assign(baseConfigStruct, object({ actions: array(any()) })), ]); +export const SIDEBAR_DEFAULT_WIDTH = 500; + @customElement("manual-automation-editor") export class HaManualAutomationEditor extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -101,6 +104,13 @@ export class HaManualAutomationEditor extends LitElement { @state() private _sidebarKey?: string; + @storage({ + key: "automation-sidebar-width", + state: false, + subscribe: false, + }) + private _sidebarWidthPx = SIDEBAR_DEFAULT_WIDTH; + @query("ha-automation-sidebar") private _sidebarElement?: HaAutomationSidebar; @queryAll("ha-automation-action, ha-automation-condition") @@ -110,6 +120,8 @@ export class HaManualAutomationEditor extends LitElement { private _previousConfig?: ManualAutomationConfig; + private _prevSidebarWidthPx?: number; + public connectedCallback() { super.connectedCallback(); window.addEventListener("paste", this._handlePaste); @@ -303,9 +315,11 @@ export class HaManualAutomationEditor extends LitElement { .hass=${this.hass} .narrow=${this.narrow} .config=${this._sidebarConfig} - @value-changed=${this._sidebarConfigChanged} .disabled=${this.disabled} .sidebarKey=${this._sidebarKey} + @value-changed=${this._sidebarConfigChanged} + @sidebar-resized=${this._resizeSidebar} + @sidebar-resizing-stopped=${this._stopResizeSidebar} > @@ -314,6 +328,12 @@ export class HaManualAutomationEditor extends LitElement { protected firstUpdated(changedProps: PropertyValues): void { super.firstUpdated(changedProps); + + this.style.setProperty( + "--sidebar-dynamic-width", + `${this._sidebarWidthPx}px` + ); + const expanded = extractSearchParam("expanded"); if (expanded === "1") { this._clearParam("expanded"); @@ -642,6 +662,31 @@ export class HaManualAutomationEditor extends LitElement { } } + private _resizeSidebar(ev) { + ev.stopPropagation(); + const delta = ev.detail.deltaInPx as number; + + // set initial resize width to add / reduce delta from it + if (!this._prevSidebarWidthPx) { + this._prevSidebarWidthPx = + this._sidebarElement?.clientWidth || SIDEBAR_DEFAULT_WIDTH; + } + + const widthPx = delta + this._prevSidebarWidthPx; + + this._sidebarWidthPx = widthPx; + + this.style.setProperty( + "--sidebar-dynamic-width", + `${this._sidebarWidthPx}px` + ); + } + + private _stopResizeSidebar(ev) { + ev.stopPropagation(); + this._prevSidebarWidthPx = undefined; + } + static get styles(): CSSResultGroup { return [ saveFabStyles, diff --git a/src/panels/config/automation/styles.ts b/src/panels/config/automation/styles.ts index b27050de79..56623282e4 100644 --- a/src/panels/config/automation/styles.ts +++ b/src/panels/config/automation/styles.ts @@ -1,5 +1,8 @@ import { css } from "lit"; +export const SIDEBAR_MIN_WIDTH = 375; +export const CONTENT_MIN_WIDTH = 350; + export const rowStyles = css` ha-icon-button { --mdc-theme-text-primary-on-background: var(--primary-text-color); @@ -109,7 +112,12 @@ export const manualEditorStyles = css` } .has-sidebar { - --sidebar-width: min(35vw, 500px); + --sidebar-width: min( + max(var(--sidebar-dynamic-width), ${SIDEBAR_MIN_WIDTH}px), + 100vw - ${CONTENT_MIN_WIDTH}px - var(--mdc-drawer-width, 0px), + var(--ha-automation-editor-max-width) - + ${CONTENT_MIN_WIDTH}px - var(--mdc-drawer-width, 0px) + ); --sidebar-gap: 16px; } diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index dda79b8e59..a742ff1afc 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -1062,6 +1062,12 @@ export class HaScriptEditor extends SubscribeMixin( return [ haStyle, css` + :host { + --ha-automation-editor-max-width: var( + --ha-automation-editor-width, + 1540px + ); + } .yaml-mode { height: 100%; display: flex; @@ -1114,7 +1120,7 @@ export class HaScriptEditor extends SubscribeMixin( } manual-script-editor { - max-width: 1540px; + max-width: var(--ha-automation-editor-max-width); padding: 0 12px; } diff --git a/src/panels/config/script/manual-script-editor.ts b/src/panels/config/script/manual-script-editor.ts index a6c805816e..086e48fd79 100644 --- a/src/panels/config/script/manual-script-editor.ts +++ b/src/panels/config/script/manual-script-editor.ts @@ -21,6 +21,7 @@ import { string, } from "superstruct"; import { ensureArray } from "../../../common/array/ensure-array"; +import { storage } from "../../../common/decorators/storage"; import { canOverrideAlphanumericInput } from "../../../common/dom/can-override-input"; import { fireEvent } from "../../../common/dom/fire_event"; import { constructUrlCurrentPath } from "../../../common/url/construct-url"; @@ -47,6 +48,7 @@ import "../automation/action/ha-automation-action"; import type HaAutomationAction from "../automation/action/ha-automation-action"; import "../automation/ha-automation-sidebar"; import type HaAutomationSidebar from "../automation/ha-automation-sidebar"; +import { SIDEBAR_DEFAULT_WIDTH } from "../automation/manual-automation-editor"; import { showPasteReplaceDialog } from "../automation/paste-replace-dialog/show-dialog-paste-replace"; import { manualEditorStyles, saveFabStyles } from "../automation/styles"; import "./ha-script-fields"; @@ -84,6 +86,13 @@ export class HaManualScriptEditor extends LitElement { @state() private _sidebarKey?: string; + @storage({ + key: "automation-sidebar-width", + state: false, + subscribe: false, + }) + private _sidebarWidthPx = SIDEBAR_DEFAULT_WIDTH; + @query("ha-script-fields") private _scriptFields?: HaScriptFields; @@ -98,6 +107,8 @@ export class HaManualScriptEditor extends LitElement { private _openFields = false; + private _prevSidebarWidthPx?: number; + public addFields() { this._openFields = true; fireEvent(this, "value-changed", { @@ -252,8 +263,10 @@ export class HaManualScriptEditor extends LitElement { .isWide=${this.isWide} .hass=${this.hass} .config=${this._sidebarConfig} - @value-changed=${this._sidebarConfigChanged} .disabled=${this.disabled} + @value-changed=${this._sidebarConfigChanged} + @sidebar-resized=${this._resizeSidebar} + @sidebar-resizing-stopped=${this._stopResizeSidebar} > @@ -262,6 +275,12 @@ export class HaManualScriptEditor extends LitElement { protected firstUpdated(changedProps: PropertyValues): void { super.firstUpdated(changedProps); + + this.style.setProperty( + "--sidebar-dynamic-width", + `${this._sidebarWidthPx}px` + ); + const expanded = extractSearchParam("expanded"); if (expanded === "1") { this._clearParam("expanded"); @@ -557,6 +576,31 @@ export class HaManualScriptEditor extends LitElement { } } + private _resizeSidebar(ev) { + ev.stopPropagation(); + const delta = ev.detail.deltaInPx as number; + + // set initial resize width to add / reduce delta from it + if (!this._prevSidebarWidthPx) { + this._prevSidebarWidthPx = + this._sidebarElement?.clientWidth || SIDEBAR_DEFAULT_WIDTH; + } + + const widthPx = delta + this._prevSidebarWidthPx; + + this._sidebarWidthPx = widthPx; + + this.style.setProperty( + "--sidebar-dynamic-width", + `${this._sidebarWidthPx}px` + ); + } + + private _stopResizeSidebar(ev) { + ev.stopPropagation(); + this._prevSidebarWidthPx = undefined; + } + static get styles(): CSSResultGroup { return [ saveFabStyles,