mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-16 14:30:36 +00:00
automation editor: resizable sidebar (#27025)
This commit is contained in:
@@ -1154,6 +1154,12 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
:host {
|
||||||
|
--ha-automation-editor-max-width: var(
|
||||||
|
--ha-automation-editor-width,
|
||||||
|
1540px
|
||||||
|
);
|
||||||
|
}
|
||||||
ha-fade-in {
|
ha-fade-in {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -1175,7 +1181,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
manual-automation-editor {
|
manual-automation-editor {
|
||||||
max-width: 1540px;
|
max-width: var(--ha-automation-editor-max-width);
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
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 "../../../components/ha-resizable-bottom-sheet";
|
||||||
import type { HaResizableBottomSheet } from "../../../components/ha-resizable-bottom-sheet";
|
import type { HaResizableBottomSheet } from "../../../components/ha-resizable-bottom-sheet";
|
||||||
import {
|
import {
|
||||||
@@ -37,9 +39,18 @@ export default class HaAutomationSidebar extends LitElement {
|
|||||||
|
|
||||||
@state() private _yamlMode = false;
|
@state() private _yamlMode = false;
|
||||||
|
|
||||||
|
@state() private _resizing = false;
|
||||||
|
|
||||||
@query("ha-resizable-bottom-sheet")
|
@query("ha-resizable-bottom-sheet")
|
||||||
private _bottomSheetElement?: HaResizableBottomSheet;
|
private _bottomSheetElement?: HaResizableBottomSheet;
|
||||||
|
|
||||||
|
private _resizeStartX = 0;
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this._unregisterResizeHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
private _renderContent() {
|
private _renderContent() {
|
||||||
// get config type
|
// get config type
|
||||||
const type = this._getType();
|
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() {
|
private _getType() {
|
||||||
@@ -207,6 +227,67 @@ export default class HaAutomationSidebar extends LitElement {
|
|||||||
(this.config as ActionSidebarConfig)?.toggleYamlMode();
|
(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`
|
static styles = css`
|
||||||
:host {
|
:host {
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
@@ -231,6 +312,28 @@ export default class HaAutomationSidebar extends LitElement {
|
|||||||
max-height: 100%;
|
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": {
|
"yaml-changed": {
|
||||||
value: unknown;
|
value: unknown;
|
||||||
};
|
};
|
||||||
|
"sidebar-resized": {
|
||||||
|
deltaInPx: number;
|
||||||
|
};
|
||||||
|
"sidebar-resizing-stopped": undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
union,
|
union,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { ensureArray } from "../../../common/array/ensure-array";
|
import { ensureArray } from "../../../common/array/ensure-array";
|
||||||
|
import { storage } from "../../../common/decorators/storage";
|
||||||
import { canOverrideAlphanumericInput } from "../../../common/dom/can-override-input";
|
import { canOverrideAlphanumericInput } from "../../../common/dom/can-override-input";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { constructUrlCurrentPath } from "../../../common/url/construct-url";
|
import { constructUrlCurrentPath } from "../../../common/url/construct-url";
|
||||||
@@ -77,6 +78,8 @@ const automationConfigStruct = union([
|
|||||||
assign(baseConfigStruct, object({ actions: array(any()) })),
|
assign(baseConfigStruct, object({ actions: array(any()) })),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export const SIDEBAR_DEFAULT_WIDTH = 500;
|
||||||
|
|
||||||
@customElement("manual-automation-editor")
|
@customElement("manual-automation-editor")
|
||||||
export class HaManualAutomationEditor extends LitElement {
|
export class HaManualAutomationEditor extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -101,6 +104,13 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
|
|
||||||
@state() private _sidebarKey?: string;
|
@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;
|
@query("ha-automation-sidebar") private _sidebarElement?: HaAutomationSidebar;
|
||||||
|
|
||||||
@queryAll("ha-automation-action, ha-automation-condition")
|
@queryAll("ha-automation-action, ha-automation-condition")
|
||||||
@@ -110,6 +120,8 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
|
|
||||||
private _previousConfig?: ManualAutomationConfig;
|
private _previousConfig?: ManualAutomationConfig;
|
||||||
|
|
||||||
|
private _prevSidebarWidthPx?: number;
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
window.addEventListener("paste", this._handlePaste);
|
window.addEventListener("paste", this._handlePaste);
|
||||||
@@ -303,9 +315,11 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.config=${this._sidebarConfig}
|
.config=${this._sidebarConfig}
|
||||||
@value-changed=${this._sidebarConfigChanged}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.sidebarKey=${this._sidebarKey}
|
.sidebarKey=${this._sidebarKey}
|
||||||
|
@value-changed=${this._sidebarConfigChanged}
|
||||||
|
@sidebar-resized=${this._resizeSidebar}
|
||||||
|
@sidebar-resizing-stopped=${this._stopResizeSidebar}
|
||||||
></ha-automation-sidebar>
|
></ha-automation-sidebar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -314,6 +328,12 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues): void {
|
protected firstUpdated(changedProps: PropertyValues): void {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
|
|
||||||
|
this.style.setProperty(
|
||||||
|
"--sidebar-dynamic-width",
|
||||||
|
`${this._sidebarWidthPx}px`
|
||||||
|
);
|
||||||
|
|
||||||
const expanded = extractSearchParam("expanded");
|
const expanded = extractSearchParam("expanded");
|
||||||
if (expanded === "1") {
|
if (expanded === "1") {
|
||||||
this._clearParam("expanded");
|
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 {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
saveFabStyles,
|
saveFabStyles,
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { css } from "lit";
|
import { css } from "lit";
|
||||||
|
|
||||||
|
export const SIDEBAR_MIN_WIDTH = 375;
|
||||||
|
export const CONTENT_MIN_WIDTH = 350;
|
||||||
|
|
||||||
export const rowStyles = css`
|
export const rowStyles = css`
|
||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||||
@@ -109,7 +112,12 @@ export const manualEditorStyles = css`
|
|||||||
}
|
}
|
||||||
|
|
||||||
.has-sidebar {
|
.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;
|
--sidebar-gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1062,6 +1062,12 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
:host {
|
||||||
|
--ha-automation-editor-max-width: var(
|
||||||
|
--ha-automation-editor-width,
|
||||||
|
1540px
|
||||||
|
);
|
||||||
|
}
|
||||||
.yaml-mode {
|
.yaml-mode {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1114,7 +1120,7 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
manual-script-editor {
|
manual-script-editor {
|
||||||
max-width: 1540px;
|
max-width: var(--ha-automation-editor-max-width);
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
string,
|
string,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { ensureArray } from "../../../common/array/ensure-array";
|
import { ensureArray } from "../../../common/array/ensure-array";
|
||||||
|
import { storage } from "../../../common/decorators/storage";
|
||||||
import { canOverrideAlphanumericInput } from "../../../common/dom/can-override-input";
|
import { canOverrideAlphanumericInput } from "../../../common/dom/can-override-input";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { constructUrlCurrentPath } from "../../../common/url/construct-url";
|
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 type HaAutomationAction from "../automation/action/ha-automation-action";
|
||||||
import "../automation/ha-automation-sidebar";
|
import "../automation/ha-automation-sidebar";
|
||||||
import type HaAutomationSidebar from "../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 { showPasteReplaceDialog } from "../automation/paste-replace-dialog/show-dialog-paste-replace";
|
||||||
import { manualEditorStyles, saveFabStyles } from "../automation/styles";
|
import { manualEditorStyles, saveFabStyles } from "../automation/styles";
|
||||||
import "./ha-script-fields";
|
import "./ha-script-fields";
|
||||||
@@ -84,6 +86,13 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
|
|
||||||
@state() private _sidebarKey?: string;
|
@state() private _sidebarKey?: string;
|
||||||
|
|
||||||
|
@storage({
|
||||||
|
key: "automation-sidebar-width",
|
||||||
|
state: false,
|
||||||
|
subscribe: false,
|
||||||
|
})
|
||||||
|
private _sidebarWidthPx = SIDEBAR_DEFAULT_WIDTH;
|
||||||
|
|
||||||
@query("ha-script-fields")
|
@query("ha-script-fields")
|
||||||
private _scriptFields?: HaScriptFields;
|
private _scriptFields?: HaScriptFields;
|
||||||
|
|
||||||
@@ -98,6 +107,8 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
|
|
||||||
private _openFields = false;
|
private _openFields = false;
|
||||||
|
|
||||||
|
private _prevSidebarWidthPx?: number;
|
||||||
|
|
||||||
public addFields() {
|
public addFields() {
|
||||||
this._openFields = true;
|
this._openFields = true;
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
@@ -252,8 +263,10 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
.isWide=${this.isWide}
|
.isWide=${this.isWide}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.config=${this._sidebarConfig}
|
.config=${this._sidebarConfig}
|
||||||
@value-changed=${this._sidebarConfigChanged}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
|
@value-changed=${this._sidebarConfigChanged}
|
||||||
|
@sidebar-resized=${this._resizeSidebar}
|
||||||
|
@sidebar-resizing-stopped=${this._stopResizeSidebar}
|
||||||
></ha-automation-sidebar>
|
></ha-automation-sidebar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -262,6 +275,12 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues): void {
|
protected firstUpdated(changedProps: PropertyValues): void {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
|
|
||||||
|
this.style.setProperty(
|
||||||
|
"--sidebar-dynamic-width",
|
||||||
|
`${this._sidebarWidthPx}px`
|
||||||
|
);
|
||||||
|
|
||||||
const expanded = extractSearchParam("expanded");
|
const expanded = extractSearchParam("expanded");
|
||||||
if (expanded === "1") {
|
if (expanded === "1") {
|
||||||
this._clearParam("expanded");
|
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 {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
saveFabStyles,
|
saveFabStyles,
|
||||||
|
|||||||
Reference in New Issue
Block a user