Automation editor mobile bottom sheet (#26680)

This commit is contained in:
Wendelin
2025-08-25 16:27:57 +02:00
committed by GitHub
parent 7ab27d620a
commit 4f4343d6c8
22 changed files with 525 additions and 123 deletions

View File

@@ -0,0 +1,268 @@
import { css, html, LitElement } from "lit";
import { customElement, query, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event";
const ANIMATION_DURATION_MS = 300;
/**
* A bottom sheet component that slides up from the bottom of the screen.
*
* The bottom sheet provides a draggable interface that allows users to resize
* the sheet by dragging the handle at the top. It supports both mouse and touch
* interactions and automatically closes when dragged below a 20% of screen height.
*
* @fires bottom-sheet-closed - Fired when the bottom sheet is closed
*
* @cssprop --ha-bottom-sheet-border-width - Border width for the sheet
* @cssprop --ha-bottom-sheet-border-style - Border style for the sheet
* @cssprop --ha-bottom-sheet-border-color - Border color for the sheet
*/
@customElement("ha-bottom-sheet")
export class HaBottomSheet extends LitElement {
@query("dialog") private _dialog!: HTMLDialogElement;
private _dragging = false;
private _dragStartY = 0;
private _initialSize = 0;
@state() private _dialogMaxViewpointHeight = 70;
@state() private _dialogViewportHeight?: number;
render() {
return html`<dialog
open
@transitionend=${this._handleTransitionEnd}
style=${styleMap({
height: this._dialogViewportHeight
? `${this._dialogViewportHeight}vh`
: "auto",
maxHeight: `${this._dialogMaxViewpointHeight}vh`,
})}
>
<div class="handle-wrapper">
<div
@mousedown=${this._handleMouseDown}
@touchstart=${this._handleTouchStart}
class="handle"
></div>
</div>
<slot></slot>
</dialog>`;
}
protected firstUpdated(changedProperties) {
super.firstUpdated(changedProperties);
this._openSheet();
}
private _openSheet() {
requestAnimationFrame(() => {
// trigger opening animation
this._dialog.classList.add("show");
});
}
public closeSheet() {
requestAnimationFrame(() => {
this._dialog.classList.remove("show");
});
}
private _handleTransitionEnd() {
if (this._dialog.classList.contains("show")) {
// after show animation is done
// - set the height to the natural height, to prevent content shift when switch content
// - set max height to 90vh, so it opens at max 70vh but can be resized to 90vh
this._dialogViewportHeight =
(this._dialog.offsetHeight / window.innerHeight) * 100;
this._dialogMaxViewpointHeight = 90;
} else {
// after close animation is done close dialog element and fire closed event
this._dialog.close();
fireEvent(this, "bottom-sheet-closed");
}
}
connectedCallback() {
super.connectedCallback();
// register event listeners for drag handling
document.addEventListener("mousemove", this._handleMouseMove);
document.addEventListener("mouseup", this._handleMouseUp);
document.addEventListener("touchmove", this._handleTouchMove, {
passive: false,
});
document.addEventListener("touchend", this._handleTouchEnd);
document.addEventListener("touchcancel", this._handleTouchEnd);
}
disconnectedCallback() {
super.disconnectedCallback();
// unregister event listeners for drag handling
document.removeEventListener("mousemove", this._handleMouseMove);
document.removeEventListener("mouseup", this._handleMouseUp);
document.removeEventListener("touchmove", this._handleTouchMove);
document.removeEventListener("touchend", this._handleTouchEnd);
document.removeEventListener("touchcancel", this._handleTouchEnd);
}
private _handleMouseDown = (ev: MouseEvent) => {
this._startDrag(ev.clientY);
};
private _handleTouchStart = (ev: TouchEvent) => {
// Prevent the browser from interpreting this as a scroll/PTR gesture.
ev.preventDefault();
this._startDrag(ev.touches[0].clientY);
};
private _startDrag(clientY: number) {
this._dragging = true;
this._dragStartY = clientY;
this._initialSize = (this._dialog.offsetHeight / window.innerHeight) * 100;
document.body.style.setProperty("cursor", "grabbing");
}
private _handleMouseMove = (ev: MouseEvent) => {
if (!this._dragging) {
return;
}
this._updateSize(ev.clientY);
};
private _handleTouchMove = (ev: TouchEvent) => {
if (!this._dragging) {
return;
}
ev.preventDefault(); // Prevent scrolling
this._updateSize(ev.touches[0].clientY);
};
private _updateSize(clientY: number) {
const deltaY = this._dragStartY - clientY;
const viewportHeight = window.innerHeight;
const deltaVh = (deltaY / viewportHeight) * 100;
// Calculate new size and clamp between 10vh and 90vh
let newSize = this._initialSize + deltaVh;
newSize = Math.max(10, Math.min(90, newSize));
// on drag down and below 20vh
if (newSize < 20 && deltaY < 0) {
this._endDrag();
this.closeSheet();
return;
}
this._dialogViewportHeight = newSize;
}
private _handleMouseUp = () => {
this._endDrag();
};
private _handleTouchEnd = () => {
this._endDrag();
};
private _endDrag() {
if (!this._dragging) {
return;
}
this._dragging = false;
document.body.style.removeProperty("cursor");
}
static styles = css`
.handle-wrapper {
position: absolute;
top: 0;
width: 100%;
padding-bottom: 2px;
display: flex;
justify-content: center;
align-items: center;
cursor: grab;
touch-action: none;
}
.handle-wrapper .handle {
height: 20px;
width: 200px;
display: flex;
justify-content: center;
align-items: center;
z-index: 7;
}
.handle-wrapper .handle::after {
content: "";
border-radius: 8px;
height: 4px;
background: var(--divider-color, #e0e0e0);
width: 80px;
}
.handle-wrapper .handle:active::after {
cursor: grabbing;
}
dialog {
height: auto;
max-height: 70vh;
min-height: 30vh;
background-color: var(
--ha-dialog-surface-background,
var(--mdc-theme-surface, #fff)
);
display: flex;
flex-direction: column;
top: 0;
inset-inline-start: 0;
position: fixed;
width: calc(100% - 4px);
max-width: 100%;
border: none;
box-shadow: var(--wa-shadow-l);
padding: 0;
margin: 0;
top: auto;
inset-inline-end: auto;
bottom: 0;
inset-inline-start: 0;
box-shadow: 0px -8px 16px rgba(0, 0, 0, 0.2);
border-top-left-radius: var(
--ha-dialog-border-radius,
var(--ha-border-radius-2xl)
);
border-top-right-radius: var(
--ha-dialog-border-radius,
var(--ha-border-radius-2xl)
);
transform: translateY(100%);
transition: transform ${ANIMATION_DURATION_MS}ms ease;
border-top-width: var(--ha-bottom-sheet-border-width);
border-right-width: var(--ha-bottom-sheet-border-width);
border-left-width: var(--ha-bottom-sheet-border-width);
border-bottom-width: 0;
border-style: var(--ha-bottom-sheet-border-style);
border-color: var(--ha-bottom-sheet-border-color);
}
dialog.show {
transform: translateY(0);
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-bottom-sheet": HaBottomSheet;
}
interface HASSDomEvents {
"bottom-sheet-closed": undefined;
}
}

View File

@@ -642,9 +642,6 @@ export default class HaAutomationActionRow extends LitElement {
}
public openSidebar(action?: Action): void {
if (this.narrow) {
this.scrollIntoView();
}
const sidebarAction = action ?? this.action;
const actionType = getAutomationActionType(sidebarAction);
@@ -670,6 +667,13 @@ export default class HaAutomationActionRow extends LitElement {
yamlMode: this._yamlMode,
} satisfies ActionSidebarConfig);
this._selected = true;
if (this.narrow) {
this.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
}
public expand() {

View File

@@ -164,12 +164,15 @@ export default class HaAutomationAction extends LitElement {
!ACTION_BUILDING_BLOCKS.includes(type)
) {
row.openSidebar();
if (this.narrow) {
row.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
} else if (!this.optionsInSidebar) {
row.expand();
}
if (this.narrow) {
row.scrollIntoView();
}
row.focus();
});
}
@@ -185,6 +188,10 @@ export default class HaAutomationAction extends LitElement {
}
private _addActionDialog() {
if (this.narrow) {
fireEvent(this, "close-sidebar");
}
showAddAutomationElementDialog(this, {
type: "action",
add: this._addAction,

View File

@@ -599,10 +599,6 @@ export default class HaAutomationConditionRow extends LitElement {
}
public openSidebar(condition?: Condition): void {
if (this.narrow) {
this.scrollIntoView();
}
const sidebarCondition = condition || this.condition;
fireEvent(this, "open-sidebar", {
save: (value) => {
@@ -626,6 +622,13 @@ export default class HaAutomationConditionRow extends LitElement {
yamlMode: this._yamlMode,
} satisfies ConditionSidebarConfig);
this._selected = true;
if (this.narrow) {
this.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
}
private _uiSupported = memoizeOne(

View File

@@ -108,12 +108,15 @@ export default class HaAutomationCondition extends LitElement {
!CONDITION_BUILDING_BLOCKS.includes(row.condition.condition)
) {
row.openSidebar();
if (this.narrow) {
row.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
} else if (!this.optionsInSidebar) {
row.expand();
}
if (this.narrow) {
row.scrollIntoView();
}
row.focus();
});
}
@@ -205,6 +208,9 @@ export default class HaAutomationCondition extends LitElement {
}
private _addConditionDialog() {
if (this.narrow) {
fireEvent(this, "close-sidebar");
}
showAddAutomationElementDialog(this, {
type: "condition",
add: this._addCondition,

View File

@@ -1,6 +1,8 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-bottom-sheet";
import type { HaBottomSheet } from "../../../components/ha-bottom-sheet";
import {
isCondition,
isScriptField,
@@ -13,14 +15,12 @@ import {
} from "../../../data/automation";
import { isTriggerList } from "../../../data/trigger";
import type { HomeAssistant } from "../../../types";
import type HaAutomationConditionEditor from "./condition/ha-automation-condition-editor";
import "./sidebar/ha-automation-sidebar-action";
import "./sidebar/ha-automation-sidebar-condition";
import "./sidebar/ha-automation-sidebar-option";
import "./sidebar/ha-automation-sidebar-script-field";
import "./sidebar/ha-automation-sidebar-script-field-selector";
import "./sidebar/ha-automation-sidebar-trigger";
import type HaAutomationTriggerEditor from "./trigger/ha-automation-trigger-editor";
@customElement("ha-automation-sidebar")
export default class HaAutomationSidebar extends LitElement {
@@ -32,92 +32,101 @@ export default class HaAutomationSidebar extends LitElement {
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public narrow = false;
@state() private _yamlMode = false;
@query(".sidebar-editor")
public editor?: HaAutomationTriggerEditor | HaAutomationConditionEditor;
protected render() {
if (!this.config) {
return nothing;
}
@query("ha-bottom-sheet") private _bottomSheetElement?: HaBottomSheet;
private _renderContent() {
// get config type
const type = this._getType();
if (type === "trigger") {
return html`
<ha-automation-sidebar-trigger
class="sidebar-content"
.hass=${this.hass}
.config=${this.config}
.isWide=${this.isWide}
.narrow=${this.narrow}
.disabled=${this.disabled}
.yamlMode=${this._yamlMode}
@toggle-yaml-mode=${this._toggleYamlMode}
@close-sidebar=${this._closeSidebar}
@close-sidebar=${this._handleCloseSidebar}
></ha-automation-sidebar-trigger>
`;
}
if (type === "condition") {
return html`
<ha-automation-sidebar-condition
class="sidebar-content"
.hass=${this.hass}
.config=${this.config}
.isWide=${this.isWide}
.narrow=${this.narrow}
.disabled=${this.disabled}
.yamlMode=${this._yamlMode}
@toggle-yaml-mode=${this._toggleYamlMode}
@close-sidebar=${this._closeSidebar}
@close-sidebar=${this._handleCloseSidebar}
></ha-automation-sidebar-condition>
`;
}
if (type === "action") {
return html`
<ha-automation-sidebar-action
class="sidebar-content"
.hass=${this.hass}
.config=${this.config}
.isWide=${this.isWide}
.narrow=${this.narrow}
.disabled=${this.disabled}
.yamlMode=${this._yamlMode}
@toggle-yaml-mode=${this._toggleYamlMode}
@close-sidebar=${this._closeSidebar}
@close-sidebar=${this._handleCloseSidebar}
></ha-automation-sidebar-action>
`;
}
if (type === "option") {
return html`
<ha-automation-sidebar-option
class="sidebar-content"
.hass=${this.hass}
.config=${this.config}
.isWide=${this.isWide}
.narrow=${this.narrow}
.disabled=${this.disabled}
@close-sidebar=${this._closeSidebar}
@close-sidebar=${this._handleCloseSidebar}
></ha-automation-sidebar-option>
`;
}
if (type === "script-field-selector") {
return html`
<ha-automation-sidebar-script-field-selector
class="sidebar-content"
.hass=${this.hass}
.config=${this.config}
.isWide=${this.isWide}
.narrow=${this.narrow}
.disabled=${this.disabled}
.yamlMode=${this._yamlMode}
@toggle-yaml-mode=${this._toggleYamlMode}
@close-sidebar=${this._closeSidebar}
@close-sidebar=${this._handleCloseSidebar}
></ha-automation-sidebar-script-field-selector>
`;
}
if (type === "script-field") {
return html`
<ha-automation-sidebar-script-field
class="sidebar-content"
.hass=${this.hass}
.config=${this.config}
.isWide=${this.isWide}
.narrow=${this.narrow}
.disabled=${this.disabled}
.yamlMode=${this._yamlMode}
@toggle-yaml-mode=${this._toggleYamlMode}
@close-sidebar=${this._closeSidebar}
@close-sidebar=${this._handleCloseSidebar}
></ha-automation-sidebar-script-field>
`;
}
@@ -125,6 +134,22 @@ export default class HaAutomationSidebar extends LitElement {
return nothing;
}
protected render() {
if (!this.config) {
return nothing;
}
if (this.narrow) {
return html`
<ha-bottom-sheet @bottom-sheet-closed=${this._closeSidebar}>
${this._renderContent()}
</ha-bottom-sheet>
`;
}
return this._renderContent();
}
private _getType() {
if (
(this.config as TriggerSidebarConfig)?.config &&
@@ -157,8 +182,17 @@ export default class HaAutomationSidebar extends LitElement {
return undefined;
}
private _closeSidebar(ev: CustomEvent) {
private _handleCloseSidebar(ev: CustomEvent) {
ev.stopPropagation();
if (this.narrow) {
this._bottomSheetElement?.closeSheet();
return;
}
this._closeSidebar();
}
private _closeSidebar() {
this.config?.close();
}
@@ -180,6 +214,15 @@ export default class HaAutomationSidebar extends LitElement {
var(--ha-border-radius-2xl)
);
border-radius: var(--ha-card-border-radius);
--ha-bottom-sheet-border-width: 2px;
--ha-bottom-sheet-border-style: solid;
--ha-bottom-sheet-border-color: var(--primary-color);
}
@media all and (max-width: 870px) {
.sidebar-content {
max-height: 100%;
}
}
`;
}

View File

@@ -3,7 +3,7 @@ import type { HassEntity } from "home-assistant-js-websocket";
import { load } from "js-yaml";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import {
any,
@@ -84,6 +84,9 @@ export class HaManualAutomationEditor extends LitElement {
@state() private _sidebarConfig?: SidebarConfig;
@query(".content")
private _contentElement?: HTMLDivElement;
private _previousConfig?: ManualAutomationConfig;
public connectedCallback() {
@@ -273,10 +276,11 @@ export class HaManualAutomationEditor extends LitElement {
class=${classMap({
sidebar: true,
hidden: !this._sidebarConfig,
overlay: !this.isWide,
overlay: !this.isWide && !this.narrow,
})}
.isWide=${this.isWide}
.hass=${this.hass}
.narrow=${this.narrow}
.config=${this._sidebarConfig}
@value-changed=${this._sidebarConfigChanged}
.disabled=${this.disabled}
@@ -313,6 +317,8 @@ export class HaManualAutomationEditor extends LitElement {
private _handleCloseSidebar() {
this._sidebarConfig = undefined;
// fix content shift when bottom rows are scrolled into view
this._contentElement?.scrollIntoView();
}
private _triggerChanged(ev: CustomEvent): void {
@@ -612,26 +618,23 @@ export class HaManualAutomationEditor extends LitElement {
.sidebar.overlay {
position: fixed;
bottom: 0;
right: 0;
height: calc(100% - 64px);
bottom: 8px;
right: 8px;
height: calc(100% - 70px);
padding: 0;
z-index: 5;
box-shadow: -8px 0 16px rgba(0, 0, 0, 0.2);
}
@media all and (max-width: 870px) {
.sidebar.overlay {
max-height: 70vh;
max-height: 70dvh;
height: auto;
width: 100%;
box-shadow: 0px -8px 16px rgba(0, 0, 0, 0.2);
.split-view {
gap: 0;
margin-right: -8px;
}
}
@media all and (max-width: 870px) {
.sidebar.overlay.hidden {
.sidebar {
height: 0;
width: 0;
flex: 0;
}
}

View File

@@ -343,10 +343,6 @@ export default class HaAutomationOptionRow extends LitElement {
}
public openSidebar(): void {
if (this.narrow) {
this.scrollIntoView();
}
fireEvent(this, "open-sidebar", {
close: () => {
this._selected = false;
@@ -359,6 +355,13 @@ export default class HaAutomationOptionRow extends LitElement {
delete: this._removeOption,
} satisfies OptionSidebarConfig);
this._selected = true;
if (this.narrow) {
this.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
}
public expand() {

View File

@@ -132,10 +132,11 @@ export default class HaAutomationOption extends LitElement {
row.updateComplete.then(() => {
if (!this.optionsInSidebar) {
row.expand();
}
if (this.narrow) {
row.scrollIntoView();
} else if (this.narrow) {
row.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
row.focus();
});

View File

@@ -34,6 +34,8 @@ export default class HaAutomationSidebarAction extends LitElement {
@property({ type: Boolean, attribute: "yaml-mode" }) public yamlMode = false;
@property({ type: Boolean }) public narrow = false;
@state() private _warnings?: string[];
@query(".sidebar-editor")
@@ -85,6 +87,7 @@ export default class HaAutomationSidebarAction extends LitElement {
.isWide=${this.isWide}
.yamlMode=${this.yamlMode}
.warnings=${this._warnings}
.narrow=${this.narrow}
>
<span slot="title">${title}</span>
<span slot="subtitle">${subtitle}</span>
@@ -133,19 +136,20 @@ export default class HaAutomationSidebarAction extends LitElement {
)}
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
</ha-md-menu-item>
${description ||
html`<ha-automation-action-editor
class="sidebar-editor"
.hass=${this.hass}
.action=${this.config.config}
.yamlMode=${this.yamlMode}
.uiSupported=${this.config.uiSupported}
@value-changed=${this._valueChangedSidebar}
sidebar
narrow
.disabled=${this.disabled}
@ui-mode-not-available=${this._handleUiModeNotAvailable}
></ha-automation-action-editor>`}
${description && !this.yamlMode
? html`<div class="description">${description}</div>`
: html`<ha-automation-action-editor
class="sidebar-editor"
.hass=${this.hass}
.action=${this.config.config}
.yamlMode=${this.yamlMode}
.uiSupported=${this.config.uiSupported}
@value-changed=${this._valueChangedSidebar}
sidebar
narrow
.disabled=${this.disabled}
@ui-mode-not-available=${this._handleUiModeNotAvailable}
></ha-automation-action-editor>`}
</ha-automation-sidebar-card>`;
}
@@ -179,6 +183,9 @@ export default class HaAutomationSidebarAction extends LitElement {
.sidebar-editor {
padding-top: 64px;
}
.description {
padding-top: 16px;
}
`;
}

View File

@@ -1,6 +1,12 @@
import { mdiClose, mdiDotsVertical } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import {
customElement,
eventOptions,
property,
query,
state,
} from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
@@ -33,6 +39,12 @@ export default class HaAutomationSidebarCard extends LitElement {
@property({ attribute: false }) public warnings?: string[];
@property({ type: Boolean }) public narrow = false;
@state() private _contentScrolled = false;
@query(".card-content") private _contentElement?: HTMLDivElement;
protected render() {
return html`
<ha-card
@@ -42,7 +54,9 @@ export default class HaAutomationSidebarCard extends LitElement {
yaml: this.yamlMode,
})}
>
<ha-dialog-header>
<ha-dialog-header
class=${classMap({ scrolled: this._contentScrolled })}
>
<ha-icon-button
slot="navigationIcon"
.label=${this.hass.localize("ui.common.close")}
@@ -56,7 +70,7 @@ export default class HaAutomationSidebarCard extends LitElement {
@click=${this._openOverflowMenu}
@keydown=${stopPropagation}
@closed=${stopPropagation}
positioning="fixed"
.positioning=${this.narrow ? "absolute" : "fixed"}
>
<ha-icon-button
slot="trigger"
@@ -74,13 +88,19 @@ export default class HaAutomationSidebarCard extends LitElement {
>
</ha-automation-editor-warning>`
: nothing}
<div class="card-content">
<div class="card-content" @scroll=${this._onScroll}>
<slot></slot>
</div>
</ha-card>
`;
}
@eventOptions({ passive: true })
private _onScroll() {
const top = this._contentElement?.scrollTop ?? 0;
this._contentScrolled = top > 0;
}
private _closeSidebar() {
fireEvent(this, "close-sidebar");
}
@@ -98,25 +118,33 @@ export default class HaAutomationSidebarCard extends LitElement {
border-width: 2px;
display: block;
}
ha-card.mobile {
border-bottom-right-radius: var(--ha-border-radius-square);
border-bottom-left-radius: var(--ha-border-radius-square);
}
@media all and (max-width: 870px) {
ha-card.mobile {
max-height: 70vh;
max-height: 70dvh;
border-width: 2px 2px 0;
border: none;
}
ha-card.mobile.yaml {
height: 70vh;
height: 70dvh;
ha-card.mobile {
border-bottom-right-radius: var(--ha-border-radius-square);
border-bottom-left-radius: var(--ha-border-radius-square);
}
}
ha-dialog-header {
border-radius: var(--ha-card-border-radius);
box-shadow: none;
transition: box-shadow 180ms ease-in-out;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
z-index: 6;
position: relative;
background-color: var(
--ha-dialog-surface-background,
var(--mdc-theme-surface, #fff)
);
}
ha-dialog-header.scrolled {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
.card-content {
@@ -130,17 +158,6 @@ export default class HaAutomationSidebarCard extends LitElement {
overflow: auto;
}
}
@media all and (max-width: 870px) {
ha-card.mobile .card-content {
max-height: calc(
70vh - 88px - max(var(--safe-area-inset-bottom), 16px)
);
max-height: calc(
70dvh - 88px - max(var(--safe-area-inset-bottom), 16px)
);
}
}
`;
}

View File

@@ -28,6 +28,8 @@ export default class HaAutomationSidebarCondition extends LitElement {
@property({ type: Boolean, attribute: "yaml-mode" }) public yamlMode = false;
@property({ type: Boolean }) public narrow = false;
@state() private _warnings?: string[];
@query(".sidebar-editor")
@@ -74,6 +76,7 @@ export default class HaAutomationSidebarCondition extends LitElement {
.isWide=${this.isWide}
.yamlMode=${this.yamlMode}
.warnings=${this._warnings}
.narrow=${this.narrow}
>
<span slot="title">${title}</span>
<span slot="subtitle">${subtitle}</span>
@@ -122,17 +125,18 @@ export default class HaAutomationSidebarCondition extends LitElement {
)}
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
</ha-md-menu-item>
${description ||
html`<ha-automation-condition-editor
class="sidebar-editor"
.hass=${this.hass}
.condition=${this.config.config}
.yamlMode=${this.yamlMode}
.uiSupported=${this.config.uiSupported}
@value-changed=${this._valueChangedSidebar}
.disabled=${this.disabled}
@ui-mode-not-available=${this._handleUiModeNotAvailable}
></ha-automation-condition-editor> `}
${description && !this.yamlMode
? html`<div class="description">${description}</div>`
: html`<ha-automation-condition-editor
class="sidebar-editor"
.hass=${this.hass}
.condition=${this.config.config}
.yamlMode=${this.yamlMode}
.uiSupported=${this.config.uiSupported}
@value-changed=${this._valueChangedSidebar}
.disabled=${this.disabled}
@ui-mode-not-available=${this._handleUiModeNotAvailable}
></ha-automation-condition-editor> `}
</ha-automation-sidebar-card>`;
}
@@ -166,6 +170,9 @@ export default class HaAutomationSidebarCondition extends LitElement {
.sidebar-editor {
padding-top: 64px;
}
.description {
padding-top: 16px;
}
`;
}

View File

@@ -16,6 +16,8 @@ export default class HaAutomationSidebarOption extends LitElement {
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public narrow = false;
@query(".sidebar-editor")
public editor?: HaAutomationConditionEditor;
@@ -37,6 +39,7 @@ export default class HaAutomationSidebarOption extends LitElement {
return html`<ha-automation-sidebar-card
.hass=${this.hass}
.isWide=${this.isWide}
.narrow=${this.narrow}
>
<span slot="title">${title}</span>
<span slot="subtitle">${subtitle}</span>
@@ -66,7 +69,7 @@ export default class HaAutomationSidebarOption extends LitElement {
)}
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
</ha-md-menu-item>
${description}
<div class="description">${description}</div>
</ha-automation-sidebar-card>`;
}
@@ -74,6 +77,9 @@ export default class HaAutomationSidebarOption extends LitElement {
.sidebar-editor {
padding-top: 64px;
}
.description {
padding-top: 16px;
}
`;
}

View File

@@ -21,6 +21,8 @@ export default class HaAutomationSidebarScriptFieldSelector extends LitElement {
@property({ type: Boolean, attribute: "yaml-mode" }) public yamlMode = false;
@property({ type: Boolean }) public narrow = false;
@state() private _warnings?: string[];
@query(".sidebar-editor")
@@ -53,6 +55,7 @@ export default class HaAutomationSidebarScriptFieldSelector extends LitElement {
.isWide=${this.isWide}
.yamlMode=${this.yamlMode}
.warnings=${this._warnings}
.narrow=${this.narrow}
>
<span slot="title">${title}</span>
<span slot="subtitle">${subtitle}</span>

View File

@@ -20,6 +20,8 @@ export default class HaAutomationSidebarScriptField extends LitElement {
@property({ type: Boolean, attribute: "yaml-mode" }) public yamlMode = false;
@property({ type: Boolean }) public narrow = false;
@state() private _warnings?: string[];
@query(".sidebar-editor")
@@ -47,6 +49,7 @@ export default class HaAutomationSidebarScriptField extends LitElement {
.isWide=${this.isWide}
.yamlMode=${this.yamlMode}
.warnings=${this._warnings}
.narrow=${this.narrow}
>
<span slot="title">${title}</span>
<ha-md-menu-item

View File

@@ -29,6 +29,8 @@ export default class HaAutomationSidebarTrigger extends LitElement {
@property({ type: Boolean, attribute: "yaml-mode" }) public yamlMode = false;
@property({ type: Boolean }) public narrow = false;
@state() private _requestShowId = false;
@state() private _warnings?: string[];
@@ -71,6 +73,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
.isWide=${this.isWide}
.yamlMode=${this.yamlMode}
.warnings=${this._warnings}
.narrow=${this.narrow}
>
<span slot="title">${title}</span>
<span slot="subtitle">${subtitle}</span>

View File

@@ -469,9 +469,6 @@ export default class HaAutomationTriggerRow extends LitElement {
}
public openSidebar(trigger?: Trigger): void {
if (this.narrow) {
this.scrollIntoView();
}
fireEvent(this, "open-sidebar", {
save: (value) => {
fireEvent(this, "value-changed", { value });
@@ -494,6 +491,13 @@ export default class HaAutomationTriggerRow extends LitElement {
yamlMode: this._yamlMode,
} satisfies TriggerSidebarConfig);
this._selected = true;
if (this.narrow) {
this.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
}
private _setClipboard() {

View File

@@ -135,6 +135,9 @@ export default class HaAutomationTrigger extends LitElement {
}
private _addTriggerDialog() {
if (this.narrow) {
fireEvent(this, "close-sidebar");
}
showAddAutomationElementDialog(this, {
type: "trigger",
add: this._addTrigger,
@@ -177,12 +180,15 @@ export default class HaAutomationTrigger extends LitElement {
row.updateComplete.then(() => {
if (this.optionsInSidebar) {
row.openSidebar();
if (this.narrow) {
row.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
} else {
row.expand();
}
if (this.narrow) {
row.scrollIntoView();
}
row.focus();
});
}

View File

@@ -187,9 +187,6 @@ export default class HaScriptFieldRow extends LitElement {
if (!selectorEditor) {
this._selected = true;
}
if (this.narrow) {
this.scrollIntoView();
}
fireEvent(this, "open-sidebar", {
save: (value) => {
@@ -216,6 +213,13 @@ export default class HaScriptFieldRow extends LitElement {
},
yamlMode: this._yamlMode,
} satisfies ScriptFieldSidebarConfig);
if (this.narrow) {
this.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
}
private _toggleYamlMode = () => {

View File

@@ -75,11 +75,14 @@ export default class HaScriptFields extends LitElement {
)!;
row.updateComplete.then(() => {
row.openSidebar();
row.focus();
if (this.narrow) {
row.scrollIntoView();
row.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
row.focus();
});
}

View File

@@ -207,6 +207,7 @@ export class HaManualScriptEditor extends LitElement {
hidden: !this._sidebarConfig,
overlay: !this.isWide,
})}
.narrow=${this.narrow}
.isWide=${this.isWide}
.hass=${this.hass}
.config=${this._sidebarConfig}
@@ -511,18 +512,14 @@ export class HaManualScriptEditor extends LitElement {
}
@media all and (max-width: 870px) {
.sidebar.overlay {
max-height: 70vh;
max-height: 70dvh;
height: auto;
width: 100%;
box-shadow: 0px -8px 16px rgba(0, 0, 0, 0.2);
.split-view {
gap: 0;
margin-right: -8px;
}
}
@media all and (max-width: 870px) {
.sidebar.overlay.hidden {
.sidebar {
height: 0;
width: 0;
flex: 0;
}
}

View File

@@ -8,6 +8,10 @@ export const waMainStyles = css`
--wa-focus-ring-offset: 2px;
--wa-focus-ring: var(--wa-focus-ring-style) var(--wa-focus-ring-width)
var(--wa-focus-ring-color);
--wa-space-l: 24px;
--wa-shadow-l: 0 8px 8px -4px rgba(0, 0, 0, 0.2);
--wa-form-control-padding-block: 0.75em;
}
`;