automation editor: resizable sidebar (#27025)

pull/27094/head
Wendelin 2025-09-18 16:45:31 +02:00 committed by GitHub
parent 912d710ae4
commit 034afd1375
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 222 additions and 6 deletions

View File

@ -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;
}

View File

@ -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`
<div
class="handle ${this._resizing ? "resizing" : ""}"
@mousedown=${this._handleMouseDown}
@touchstart=${this._handleMouseDown}
>
${this._resizing ? html`<div class="indicator"></div>` : nothing}
</div>
${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;
}
}

View File

@ -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}
></ha-automation-sidebar>
</div>
</div>
@ -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,

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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}
></ha-automation-sidebar>
</div>
</div>
@ -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,