Automation editor: New choose default styles and style fixes (#26708)

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
This commit is contained in:
Wendelin
2025-08-27 13:20:25 +02:00
committed by GitHub
parent 673ca8ba4b
commit 22966485c7
21 changed files with 467 additions and 585 deletions

View File

@@ -89,12 +89,12 @@ export class HaActionSelector extends SubscribeMixin(LitElement) {
static styles = css`
ha-automation-action {
display: block;
margin-bottom: 16px;
}
label {
display: block;
margin-bottom: 4px;
font-weight: var(--ha-font-weight-medium);
color: var(--secondary-text-color);
}
`;
}

View File

@@ -53,6 +53,7 @@ export class HaConditionSelector extends LitElement {
display: block;
margin-bottom: 4px;
font-weight: var(--ha-font-weight-medium);
color: var(--secondary-text-color);
}
`;
}

View File

@@ -607,6 +607,7 @@ export interface OptionSidebarConfig extends BaseSidebarConfig {
close: () => void;
rename: () => void;
duplicate: () => void;
defaultOption?: boolean;
}
export interface ScriptFieldSidebarConfig extends BaseSidebarConfig {

View File

@@ -24,6 +24,7 @@ import {
VIRTUAL_ACTIONS,
showAddAutomationElementDialog,
} from "../show-add-automation-element-dialog";
import { automationRowsStyles } from "../styles";
import type HaAutomationActionRow from "./ha-automation-action-row";
import { getAutomationActionType } from "./ha-automation-action-row";
@@ -89,7 +90,7 @@ export default class HaAutomationAction extends LitElement {
@item-added=${this._actionAdded}
@item-removed=${this._actionRemoved}
>
<div class="actions">
<div class="rows">
${repeat(
this.actions,
(action) => this._getKey(action),
@@ -335,44 +336,14 @@ export default class HaAutomationAction extends LitElement {
});
}
static styles = css`
.actions {
padding: 16px 0 16px 16px;
margin: -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
:host([root]) .actions {
padding-right: 8px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
}
.sortable-drag {
background: none;
}
ha-automation-action-row {
display: block;
scroll-margin-top: 48px;
}
.handle {
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
.handle ha-svg-icon {
pointer-events: none;
height: 24px;
}
.buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
order: 1;
}
`;
static styles = [
automationRowsStyles,
css`
:host([root]) .rows {
padding-right: 8px;
}
`,
];
}
declare global {

View File

@@ -1,15 +1,16 @@
import { type CSSResultGroup, LitElement, css, html } from "lit";
import { type CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { ensureArray } from "../../../../../common/array/ensure-array";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-button";
import type { Action, ChooseAction, Option } from "../../../../../data/script";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import "../../option/ha-automation-option";
import type HaAutomationOption from "../../option/ha-automation-option";
import "../../option/ha-automation-option-row";
import type HaAutomationOptionRow from "../../option/ha-automation-option-row";
import { indentStyle } from "../../styles";
import "../ha-automation-action";
import type HaAutomationAction from "../ha-automation-action";
import type { ActionElement } from "../ha-automation-action-row";
@customElement("ha-automation-action-choose")
@@ -28,7 +29,8 @@ export class HaChooseAction extends LitElement implements ActionElement {
@query("ha-automation-option") private _optionElement?: HaAutomationOption;
@query("ha-automation-action") private _actionElement?: HaAutomationAction;
@query("ha-automation-option-row")
private _defaultOptionRowElement?: HaAutomationOptionRow;
public static get defaultConfig(): ChooseAction {
return { choose: [{ conditions: [], sequence: [] }] };
@@ -47,42 +49,29 @@ export class HaChooseAction extends LitElement implements ActionElement {
.hass=${this.hass}
.narrow=${this.narrow}
.optionsInSidebar=${this.indent}
.showDefaultActions=${this._showDefault || !!action.default}
@show-default-actions=${this._addDefault}
></ha-automation-option>
${this._showDefault || action.default
? html`
<h4>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.default"
)}:
</h4>
<ha-automation-action
.actions=${ensureArray(action.default) || []}
.disabled=${this.disabled}
@value-changed=${this._defaultChanged}
.hass=${this.hass}
<ha-automation-option-row
.defaultActions=${(ensureArray(action.default) || []) as Action[]}
.narrow=${this.narrow}
.disabled=${this.disabled}
.hass=${this.hass}
.optionsInSidebar=${this.indent}
></ha-automation-action>
@value-changed=${this._defaultChanged}
></ha-automation-option-row>
`
: html`
<div class="link-button-row">
<button
class="link"
@click=${this._addDefault}
.disabled=${this.disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.add_default"
)}
</button>
</div>
`}
: nothing}
`;
}
private _addDefault() {
private async _addDefault() {
this._showDefault = true;
await this._defaultOptionRowElement?.updateComplete;
this._defaultOptionRowElement?.expand();
}
private _optionsChanged(ev: CustomEvent) {
@@ -112,20 +101,26 @@ export class HaChooseAction extends LitElement implements ActionElement {
public expandAll() {
this._optionElement?.expandAll();
this._actionElement?.expandAll();
this._defaultOptionRowElement?.expandAll();
}
public collapseAll() {
this._optionElement?.collapseAll();
this._actionElement?.collapseAll();
this._defaultOptionRowElement?.collapseAll();
}
static get styles(): CSSResultGroup {
return [
haStyle,
indentStyle,
css`
.link-button-row {
padding: 14px 14px 0 14px;
ha-automation-option-row {
display: block;
margin-top: 24px;
}
h3 {
font-size: inherit;
font-weight: inherit;
}
`,
];

View File

@@ -1,12 +1,6 @@
import type { CSSResultGroup } from "lit";
import { css, html, LitElement } from "lit";
import {
customElement,
property,
query,
queryAll,
state,
} from "lit/decorators";
import { customElement, property, query, queryAll } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-textfield";
import type { Action, IfAction } from "../../../../../data/script";
@@ -30,8 +24,6 @@ export class HaIfAction extends LitElement implements ActionElement {
@property({ type: Boolean }) public indent = false;
@state() private _showElse = false;
@query("ha-automation-condition")
private _conditionElement?: HaAutomationCondition;
@@ -49,11 +41,11 @@ export class HaIfAction extends LitElement implements ActionElement {
const action = this.action;
return html`
<h3>
<h4>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.if.if"
)}*:
</h3>
)}:
</h4>
<ha-automation-condition
.conditions=${action.if ?? []}
.disabled=${this.disabled}
@@ -63,11 +55,11 @@ export class HaIfAction extends LitElement implements ActionElement {
.optionsInSidebar=${this.indent}
></ha-automation-condition>
<h3>
<h4>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.if.then"
)}*:
</h3>
)}:
</h4>
<ha-automation-action
.actions=${action.then ?? []}
.disabled=${this.disabled}
@@ -76,40 +68,22 @@ export class HaIfAction extends LitElement implements ActionElement {
.narrow=${this.narrow}
.optionsInSidebar=${this.indent}
></ha-automation-action>
${this._showElse || action.else
? html`
<h3>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.if.else"
)}:
</h3>
<ha-automation-action
.actions=${action.else || []}
.disabled=${this.disabled}
@value-changed=${this._elseChanged}
.hass=${this.hass}
.narrow=${this.narrow}
.optionsInSidebar=${this.indent}
></ha-automation-action>
`
: html`<div class="link-button-row">
<button
class="link"
@click=${this._addElse}
.disabled=${this.disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.if.add_else"
)}
</button>
</div>`}
<h4>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.if.else"
)}:
</h4>
<ha-automation-action
.actions=${action.else || []}
.disabled=${this.disabled}
@value-changed=${this._elseChanged}
.hass=${this.hass}
.narrow=${this.narrow}
.optionsInSidebar=${this.indent}
></ha-automation-action>
`;
}
private _addElse() {
this._showElse = true;
}
private _ifChanged(ev: CustomEvent) {
ev.stopPropagation();
const value = ev.detail.value as Condition[];
@@ -134,7 +108,6 @@ export class HaIfAction extends LitElement implements ActionElement {
private _elseChanged(ev: CustomEvent) {
ev.stopPropagation();
this._showElse = true;
const elseAction = ev.detail.value as Action[];
const newValue: IfAction = {
...this.action,
@@ -160,8 +133,12 @@ export class HaIfAction extends LitElement implements ActionElement {
return [
haStyle,
css`
.link-button-row {
padding: 14px;
h4 {
color: var(--secondary-text-color);
margin-bottom: 8px;
}
h4:first-child {
margin-top: 0;
}
`,
];

View File

@@ -133,6 +133,7 @@ export default class HaAutomationConditionEditor extends LitElement {
margin-right: 0;
padding: 0;
border-left: none;
border-bottom: none;
}
`,
];

View File

@@ -1,7 +1,7 @@
import { mdiDrag, mdiPlus } from "@mdi/js";
import deepClone from "deep-clone-simple";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, queryAll, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { storage } from "../../../../common/decorators/storage";
@@ -22,6 +22,7 @@ import {
PASTE_VALUE,
showAddAutomationElementDialog,
} from "../show-add-automation-element-dialog";
import { automationRowsStyles } from "../styles";
import "./ha-automation-condition-row";
import type HaAutomationConditionRow from "./ha-automation-condition-row";
@@ -151,7 +152,7 @@ export default class HaAutomationCondition extends LitElement {
@item-added=${this._conditionAdded}
@item-removed=${this._conditionRemoved}
>
<div class="conditions">
<div class="rows">
${repeat(
this.conditions.filter((c) => typeof c === "object"),
(condition) => this._getKey(condition),
@@ -354,44 +355,14 @@ export default class HaAutomationCondition extends LitElement {
});
}
static styles = css`
.conditions {
padding: 16px 0 16px 16px;
margin: -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
:host([root]) .conditions {
padding-right: 8px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
}
.sortable-drag {
background: none;
}
ha-automation-condition-row {
display: block;
scroll-margin-top: 48px;
}
.handle {
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
.handle ha-svg-icon {
pointer-events: none;
height: 24px;
}
.buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
order: 1;
}
`;
static styles = [
automationRowsStyles,
css`
:host([root]) .rows {
padding-right: 8px;
}
`,
];
}
declare global {

View File

@@ -50,7 +50,7 @@ import "./condition/ha-automation-condition";
import type HaAutomationCondition from "./condition/ha-automation-condition";
import "./ha-automation-sidebar";
import { showPasteReplaceDialog } from "./paste-replace-dialog/show-dialog-paste-replace";
import { saveFabStyles } from "./styles";
import { manualEditorStyles, saveFabStyles } from "./styles";
import "./trigger/ha-automation-trigger";
const baseConfigStruct = object({
@@ -266,7 +266,12 @@ export class HaManualAutomationEditor extends LitElement {
protected render() {
return html`
<div class="split-view">
<div
class=${classMap({
"split-view": true,
"sidebar-hidden": !this._sidebarConfig,
})}
>
<div class="content-wrapper">
<div class="content">${this._renderContent()}</div>
<ha-fab
@@ -283,7 +288,6 @@ export class HaManualAutomationEditor extends LitElement {
<ha-automation-sidebar
class=${classMap({
sidebar: true,
hidden: !this._sidebarConfig,
overlay: !this.isWide && !this.narrow,
rtl: computeRTL(this.hass),
})}
@@ -619,81 +623,8 @@ export class HaManualAutomationEditor extends LitElement {
static get styles(): CSSResultGroup {
return [
saveFabStyles,
manualEditorStyles,
css`
:host {
display: block;
}
.split-view {
display: flex;
flex-direction: row;
height: 100%;
position: relative;
gap: 16px;
}
.content-wrapper {
position: relative;
flex: 6;
}
.content {
padding: 32px 16px 64px 0;
height: calc(100vh - 153px);
height: calc(100dvh - 153px);
overflow-y: auto;
overflow-x: hidden;
}
.sidebar {
padding: 12px 0;
flex: 4;
height: calc(100vh - 81px);
height: calc(100dvh - 81px);
width: 40%;
}
.sidebar.hidden {
border-color: transparent;
border-width: 0;
overflow: hidden;
flex: 0;
visibility: hidden;
}
.sidebar.overlay {
position: fixed;
bottom: 8px;
right: 8px;
height: calc(100% - 70px);
padding: 0;
z-index: 5;
box-shadow: -8px 0 16px rgba(0, 0, 0, 0.2);
}
.sidebar.overlay.rtl {
right: unset;
left: 8px;
}
@media all and (max-width: 870px) {
.split-view {
gap: 0;
margin-right: -8px;
}
.sidebar {
height: 0;
width: 0;
flex: 0;
}
}
.sidebar.overlay.hidden {
width: 0;
}
.description {
margin: 0;
}
p {
margin-top: 0;
}
@@ -711,9 +642,6 @@ export class HaManualAutomationEditor extends LitElement {
flex: 1;
margin-bottom: 8px;
}
.header a {
color: var(--secondary-text-color);
}
.header .small {
font-size: small;
font-weight: var(--ha-font-weight-normal);

View File

@@ -46,7 +46,9 @@ import { editorStyles, indentStyle, rowStyles } from "../styles";
export default class HaAutomationOptionRow extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public option!: Option;
@property({ attribute: false }) public option?: Option;
@property({ attribute: false }) public defaultActions?: Action[];
@property({ type: Boolean }) public narrow = false;
@@ -85,7 +87,7 @@ export default class HaAutomationOptionRow extends LitElement {
}
private _getDescription() {
const conditions = ensureArray<Condition | string>(this.option.conditions);
const conditions = ensureArray<Condition | string>(this.option!.conditions);
if (!conditions || conditions.length === 0) {
return this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.no_conditions"
@@ -109,89 +111,102 @@ export default class HaAutomationOptionRow extends LitElement {
private _renderRow() {
return html`
<h3 slot="header">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.option",
{ number: this.index + 1 }
)}:
${this.option.alias || (this._expanded ? "" : this._getDescription())}
${this.option
? `${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.option",
{ number: this.index + 1 }
)}: ${this.option.alias || (this._expanded ? "" : this._getDescription())}`
: this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.default"
)}
</h3>
<slot name="icons" slot="icons"></slot>
<ha-md-button-menu
slot="icons"
@click=${preventDefaultStopPropagation}
@closed=${stopPropagation}
@keydown=${stopPropagation}
positioning="fixed"
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
${!this.optionsInSidebar
? html`
<ha-md-menu-item
@click=${this._renameOption}
.disabled=${this.disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.rename"
)}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</ha-md-menu-item>
<ha-md-menu-item
@click=${this._duplicateOption}
.disabled=${this.disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</ha-md-menu-item>
`
: nothing}
<ha-md-menu-item
@click=${this._moveUp}
.disabled=${this.disabled || this.first}
>
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
<ha-svg-icon slot="graphic" .path=${mdiArrowUp}></ha-svg-icon>
</ha-md-menu-item>
<ha-md-menu-item
@click=${this._moveDown}
.disabled=${this.disabled || this.last}
>
${this.hass.localize("ui.panel.config.automation.editor.move_down")}
<ha-svg-icon slot="graphic" .path=${mdiArrowDown}></ha-svg-icon>
</ha-md-menu-item>
${!this.optionsInSidebar
? html`<ha-md-menu-item
@click=${this._removeOption}
class="warning"
.disabled=${this.disabled}
${this.option
? html`
<ha-md-button-menu
slot="icons"
@click=${preventDefaultStopPropagation}
@closed=${stopPropagation}
@keydown=${stopPropagation}
positioning="fixed"
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
)}
<ha-svg-icon
class="warning"
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</ha-md-menu-item>`
: nothing}
</ha-md-button-menu>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
${!this.optionsInSidebar
? html`
<ha-md-menu-item
@click=${this._renameOption}
.disabled=${this.disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.rename"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiRenameBox}
></ha-svg-icon>
</ha-md-menu-item>
<ha-md-menu-item
@click=${this._duplicateOption}
.disabled=${this.disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</ha-md-menu-item>
`
: nothing}
<ha-md-menu-item
@click=${this._moveUp}
.disabled=${this.disabled || this.first}
>
${this.hass.localize(
"ui.panel.config.automation.editor.move_up"
)}
<ha-svg-icon slot="graphic" .path=${mdiArrowUp}></ha-svg-icon>
</ha-md-menu-item>
<ha-md-menu-item
@click=${this._moveDown}
.disabled=${this.disabled || this.last}
>
${this.hass.localize(
"ui.panel.config.automation.editor.move_down"
)}
<ha-svg-icon slot="graphic" .path=${mdiArrowDown}></ha-svg-icon>
</ha-md-menu-item>
${!this.optionsInSidebar
? html`<ha-md-menu-item
@click=${this._removeOption}
class="warning"
.disabled=${this.disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
)}
<ha-svg-icon
class="warning"
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</ha-md-menu-item>`
: nothing}
</ha-md-button-menu>
`
: nothing}
${!this.optionsInSidebar ? this._renderContent() : nothing}
`;
}
@@ -202,29 +217,39 @@ export default class HaAutomationOptionRow extends LitElement {
"card-content": true,
indent: this.optionsInSidebar,
selected: this._selected,
hidden: this._collapsed,
hidden: this.optionsInSidebar && this._collapsed,
})}
>
<h4 class="conditions">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.conditions"
)}:
</h4>
<ha-automation-condition
.conditions=${ensureArray<string | Condition>(this.option.conditions)}
.disabled=${this.disabled}
.hass=${this.hass}
.narrow=${this.narrow}
@value-changed=${this._conditionChanged}
.optionsInSidebar=${this.optionsInSidebar}
></ha-automation-condition>
<h4 class="actions">
${this.option
? html`
<h4 class="top">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.conditions"
)}:
</h4>
<ha-automation-condition
.conditions=${ensureArray<string | Condition>(
this.option.conditions
)}
.disabled=${this.disabled}
.hass=${this.hass}
.narrow=${this.narrow}
@value-changed=${this._conditionChanged}
.optionsInSidebar=${this.optionsInSidebar}
></ha-automation-condition>
`
: nothing}
<h4 class=${this.option ? "" : "top"}>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.sequence"
)}:
</h4>
<ha-automation-action
.actions=${ensureArray(this.option.sequence) || []}
.actions=${(this.option
? ensureArray(this.option.sequence) || []
: this.defaultActions
? ensureArray(this.defaultActions) || []
: []) as Action[]}
.disabled=${this.disabled}
.hass=${this.hass}
.narrow=${this.narrow}
@@ -235,7 +260,7 @@ export default class HaAutomationOptionRow extends LitElement {
}
protected render() {
if (!this.option) return nothing;
if (!this.option && !this.defaultActions) return nothing;
return html`
<ha-card outlined class=${this._selected ? "selected" : ""}>
@@ -307,7 +332,7 @@ export default class HaAutomationOptionRow extends LitElement {
),
inputType: "string",
placeholder: capitalizeFirstLetter(this._getDescription()),
defaultValue: this.option.alias,
defaultValue: this.option!.alias,
confirmText: this.hass.localize("ui.common.submit"),
});
if (alias !== null) {
@@ -333,6 +358,9 @@ export default class HaAutomationOptionRow extends LitElement {
}
private _actionChanged(ev: CustomEvent) {
if (this.defaultActions) {
return;
}
ev.stopPropagation();
const actions = ev.detail.value as Action[];
const value = { ...this.option, sequence: actions };
@@ -364,6 +392,7 @@ export default class HaAutomationOptionRow extends LitElement {
toggleYamlMode: () => false, // no yaml mode for options
delete: this._removeOption,
duplicate: this._duplicateOption,
defaultOption: !!this.defaultActions,
} satisfies OptionSidebarConfig);
this._selected = true;
this._collapsed = false;
@@ -418,12 +447,14 @@ export default class HaAutomationOptionRow extends LitElement {
li[role="separator"] {
border-bottom-color: var(--divider-color);
}
h4.conditions {
margin-top: 0;
h4 {
color: var(--ha-color-text-secondary);
}
h4 {
margin-bottom: 8px;
}
h4.actions {
margin-bottom: 8px;
h4.top {
margin-top: 0;
}
`,
];

View File

@@ -1,7 +1,7 @@
import { mdiDrag, mdiPlus } from "@mdi/js";
import deepClone from "deep-clone-simple";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { LitElement, html, nothing } from "lit";
import { customElement, property, queryAll, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { storage } from "../../../../common/decorators/storage";
@@ -14,6 +14,7 @@ import "../../../../components/ha-svg-icon";
import type { AutomationClipboard } from "../../../../data/automation";
import type { Option } from "../../../../data/script";
import type { HomeAssistant } from "../../../../types";
import { automationRowsStyles } from "../styles";
import "./ha-automation-option-row";
import type HaAutomationOptionRow from "./ha-automation-option-row";
@@ -30,6 +31,9 @@ export default class HaAutomationOption extends LitElement {
@property({ type: Boolean, attribute: "sidebar" }) public optionsInSidebar =
false;
@property({ type: Boolean, attribute: "show-default" })
public showDefaultActions = false;
@state() private _showReorder = false;
@state()
@@ -75,7 +79,7 @@ export default class HaAutomationOption extends LitElement {
@item-added=${this._optionAdded}
@item-removed=${this._optionRemoved}
>
<div class="options">
<div class="rows">
${repeat(
this.options,
(option) => this._getKey(option),
@@ -117,6 +121,19 @@ export default class HaAutomationOption extends LitElement {
"ui.panel.config.automation.editor.actions.type.choose.add_option"
)}
</ha-button>
${!this.showDefaultActions
? html`<ha-button
appearance="plain"
size="small"
.disabled=${this.disabled}
@click=${this._showDefaultActions}
>
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.add_default"
)}
</ha-button>`
: nothing}
</div>
</div>
</ha-sortable>
@@ -248,45 +265,19 @@ export default class HaAutomationOption extends LitElement {
});
}
static styles = css`
.options {
padding: 16px 0 16px 16px;
margin: -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
}
.sortable-drag {
background: none;
}
ha-automation-option-row {
display: block;
scroll-margin-top: 48px;
}
.handle {
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
.handle ha-svg-icon {
pointer-events: none;
height: 24px;
}
.buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
order: 1;
}
`;
private _showDefaultActions = () => {
fireEvent(this, "show-default-actions");
};
static styles = automationRowsStyles;
}
declare global {
interface HTMLElementTagNameMap {
"ha-automation-option": HaAutomationOption;
}
interface HASSDomEvents {
"show-default-actions": undefined;
}
}

View File

@@ -9,7 +9,7 @@ import {
mdiRenameBox,
mdiStopCircleOutline,
} from "@mdi/js";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { handleStructError } from "../../../../common/structs/handle-errors";
@@ -23,6 +23,7 @@ import type { HomeAssistant } from "../../../../types";
import type HaAutomationConditionEditor from "../action/ha-automation-action-editor";
import { getAutomationActionType } from "../action/ha-automation-action-row";
import { getRepeatType } from "../action/types/ha-automation-action-repeat";
import { sidebarEditorStyles } from "../styles";
import "../trigger/ha-automation-trigger-editor";
import "./ha-automation-sidebar-card";
@@ -223,14 +224,7 @@ export default class HaAutomationSidebarAction extends LitElement {
fireEvent(this, "toggle-yaml-mode");
};
static styles = css`
.sidebar-editor {
padding-top: 64px;
}
.description {
padding-top: 16px;
}
`;
static styles = sidebarEditorStyles;
}
declare global {

View File

@@ -9,7 +9,7 @@ import {
mdiRenameBox,
mdiStopCircleOutline,
} from "@mdi/js";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { handleStructError } from "../../../../common/structs/handle-errors";
@@ -18,6 +18,7 @@ import { CONDITION_BUILDING_BLOCKS } from "../../../../data/condition";
import type { HomeAssistant } from "../../../../types";
import "../condition/ha-automation-condition-editor";
import type HaAutomationConditionEditor from "../condition/ha-automation-condition-editor";
import { sidebarEditorStyles } from "../styles";
import "./ha-automation-sidebar-card";
@customElement("ha-automation-sidebar-condition")
@@ -211,14 +212,7 @@ export default class HaAutomationSidebarCondition extends LitElement {
fireEvent(this, "toggle-yaml-mode");
};
static styles = css`
.sidebar-editor {
padding-top: 64px;
}
.description {
padding-top: 16px;
}
`;
static styles = sidebarEditorStyles;
}
declare global {

View File

@@ -1,9 +1,10 @@
import { mdiContentDuplicate, mdiDelete, mdiRenameBox } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import type { OptionSidebarConfig } from "../../../../data/automation";
import type { HomeAssistant } from "../../../../types";
import type HaAutomationConditionEditor from "../action/ha-automation-action-editor";
import { sidebarEditorStyles } from "../styles";
import "./ha-automation-sidebar-card";
@customElement("ha-automation-sidebar-option")
@@ -29,11 +30,11 @@ export default class HaAutomationSidebarOption extends LitElement {
);
const title = this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.option_label"
`ui.panel.config.automation.editor.actions.type.choose.${this.config.defaultOption ? "default_" : ""}option_label`
);
const description = this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.option_description"
`ui.panel.config.automation.editor.actions.type.choose.${this.config.defaultOption ? "default_" : ""}option_description`
);
return html`<ha-automation-sidebar-card
@@ -43,55 +44,56 @@ export default class HaAutomationSidebarOption extends LitElement {
>
<span slot="title">${title}</span>
<span slot="subtitle">${subtitle}</span>
<ha-md-menu-item
slot="menu-items"
.clickAction=${this.config.rename}
.disabled=${!!disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.rename"
)}
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
</ha-md-menu-item>
${this.config.defaultOption
? html`<span slot="overflow-menu"></span>`
: html`
<ha-md-menu-item
slot="menu-items"
.clickAction=${this.config.rename}
.disabled=${!!disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.rename"
)}
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
</ha-md-menu-item>
<ha-md-menu-item
slot="menu-items"
@click=${this.config.duplicate}
.disabled=${this.disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</ha-md-menu-item>
<ha-md-divider
slot="menu-items"
role="separator"
tabindex="-1"
></ha-md-divider>
<ha-md-menu-item
slot="menu-items"
.clickAction=${this.config.delete}
.disabled=${this.disabled}
class="warning"
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
)}
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
</ha-md-menu-item>
`}
<ha-md-menu-item
slot="menu-items"
@click=${this.config.duplicate}
.disabled=${this.disabled}
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon slot="graphic" .path=${mdiContentDuplicate}></ha-svg-icon>
</ha-md-menu-item>
<ha-md-divider
slot="menu-items"
role="separator"
tabindex="-1"
></ha-md-divider>
<ha-md-menu-item
slot="menu-items"
.clickAction=${this.config.delete}
.disabled=${this.disabled}
class="warning"
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
)}
<ha-svg-icon slot="start" .path=${mdiDelete}></ha-svg-icon>
</ha-md-menu-item>
<div class="description">${description}</div>
</ha-automation-sidebar-card>`;
}
static styles = css`
.sidebar-editor {
padding-top: 64px;
}
.description {
padding-top: 16px;
}
`;
static styles = sidebarEditorStyles;
}
declare global {

View File

@@ -1,5 +1,5 @@
import { mdiDelete, mdiPlaylistEdit } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { LocalizeKeys } from "../../../../common/translations/localize";
@@ -7,6 +7,7 @@ import type { ScriptFieldSidebarConfig } from "../../../../data/automation";
import type { HomeAssistant } from "../../../../types";
import "../../script/ha-script-field-selector-editor";
import type HaAutomationConditionEditor from "../action/ha-automation-action-editor";
import { sidebarEditorStyles } from "../styles";
import "./ha-automation-sidebar-card";
@customElement("ha-automation-sidebar-script-field-selector")
@@ -119,11 +120,7 @@ export default class HaAutomationSidebarScriptFieldSelector extends LitElement {
fireEvent(this, "toggle-yaml-mode");
};
static styles = css`
.sidebar-editor {
padding-top: 64px;
}
`;
static styles = sidebarEditorStyles;
}
declare global {

View File

@@ -1,11 +1,12 @@
import { mdiDelete, mdiPlaylistEdit } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { ScriptFieldSidebarConfig } from "../../../../data/automation";
import type { HomeAssistant } from "../../../../types";
import "../../script/ha-script-field-editor";
import type HaAutomationConditionEditor from "../action/ha-automation-action-editor";
import { sidebarEditorStyles } from "../styles";
import "./ha-automation-sidebar-card";
@customElement("ha-automation-sidebar-script-field")
@@ -113,11 +114,7 @@ export default class HaAutomationSidebarScriptField extends LitElement {
fireEvent(this, "toggle-yaml-mode");
};
static styles = css`
.sidebar-editor {
padding-top: 64px;
}
`;
static styles = sidebarEditorStyles;
}
declare global {

View File

@@ -9,13 +9,14 @@ import {
mdiRenameBox,
mdiStopCircleOutline,
} from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { handleStructError } from "../../../../common/structs/handle-errors";
import type { TriggerSidebarConfig } from "../../../../data/automation";
import { isTriggerList } from "../../../../data/trigger";
import type { HomeAssistant } from "../../../../types";
import { sidebarEditorStyles } from "../styles";
import "../trigger/ha-automation-trigger-editor";
import type HaAutomationTriggerEditor from "../trigger/ha-automation-trigger-editor";
import "./ha-automation-sidebar-card";
@@ -227,11 +228,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
this._requestShowId = true;
};
static styles = css`
.sidebar-editor {
padding-top: 64px;
}
`;
static styles = sidebarEditorStyles;
}
declare global {

View File

@@ -104,3 +104,133 @@ export const saveFabStyles = css`
bottom: 16px;
}
`;
export const manualEditorStyles = css`
:host {
display: block;
}
.split-view {
display: flex;
flex-direction: row;
height: 100%;
position: relative;
gap: 16px;
}
.split-view.sidebar-hidden {
gap: 0;
}
.content-wrapper {
position: relative;
flex: 6;
}
.content {
padding: 32px 16px 64px 0;
height: calc(100vh - 153px);
height: calc(100dvh - 153px);
overflow-y: auto;
overflow-x: hidden;
}
.sidebar {
padding: 12px 0;
flex: 4;
height: calc(100vh - 81px);
height: calc(100dvh - 81px);
width: 40%;
}
.split-view.sidebar-hidden .sidebar {
border-color: transparent;
border-width: 0;
overflow: hidden;
flex: 0;
visibility: hidden;
}
.sidebar.overlay {
position: fixed;
bottom: 8px;
right: 8px;
height: calc(100% - 70px);
padding: 0;
z-index: 5;
box-shadow: -8px 0 16px rgba(0, 0, 0, 0.2);
}
.sidebar.overlay.rtl {
right: unset;
left: 8px;
}
@media all and (max-width: 870px) {
.split-view {
gap: 0;
margin-right: -8px;
}
.sidebar {
height: 0;
width: 0;
flex: 0;
}
}
.split-view.sidebar-hidden .sidebar.overlay {
width: 0;
}
.description {
margin: 0;
}
.header a {
color: var(--secondary-text-color);
}
`;
export const automationRowsStyles = css`
.rows {
padding: 16px 0 16px 16px;
margin: -16px;
margin-right: -20px;
display: flex;
flex-direction: column;
gap: 16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
}
.sortable-drag {
background: none;
}
ha-automation-action-row {
display: block;
scroll-margin-top: 48px;
}
.handle {
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
.handle ha-svg-icon {
pointer-events: none;
height: 24px;
}
.buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
order: 1;
}
`;
export const sidebarEditorStyles = css`
.sidebar-editor {
display: block;
padding-top: 16px;
}
.description {
padding-top: 16px;
}
`;

View File

@@ -1,7 +1,7 @@
import { mdiDrag, mdiPlus } from "@mdi/js";
import deepClone from "deep-clone-simple";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { storage } from "../../../../common/decorators/storage";
@@ -23,6 +23,7 @@ import {
PASTE_VALUE,
showAddAutomationElementDialog,
} from "../show-add-automation-element-dialog";
import { automationRowsStyles } from "../styles";
import "./ha-automation-trigger-row";
import type HaAutomationTriggerRow from "./ha-automation-trigger-row";
@@ -85,7 +86,7 @@ export default class HaAutomationTrigger extends LitElement {
@item-added=${this._triggerAdded}
@item-removed=${this._triggerRemoved}
>
<div class="triggers">
<div class="rows">
${repeat(
this.triggers,
(trigger) => this._getKey(trigger),
@@ -195,10 +196,11 @@ export default class HaAutomationTrigger extends LitElement {
}
public expandAll() {
const rows = this.shadowRoot!.querySelectorAll<HaAutomationTriggerRow>(
"ha-automation-trigger-row"
)!;
rows.forEach((row) => {
const triggerRows =
this.shadowRoot!.querySelectorAll<HaAutomationTriggerRow>(
"ha-automation-trigger-row"
)!;
triggerRows.forEach((row) => {
row.expand();
});
}
@@ -300,44 +302,14 @@ export default class HaAutomationTrigger extends LitElement {
});
}
static styles = css`
.triggers {
padding: 16px 0 16px 16px;
margin: -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
:host([root]) .triggers {
padding-right: 8px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
}
.sortable-drag {
background: none;
}
ha-automation-trigger-row {
display: block;
scroll-margin-top: 48px;
}
.handle {
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
.handle ha-svg-icon {
pointer-events: none;
height: 24px;
}
.buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
order: 1;
}
`;
static styles = [
automationRowsStyles,
css`
:host([root]) .rows {
padding-right: 8px;
}
`,
];
}
declare global {

View File

@@ -39,7 +39,7 @@ import "../automation/action/ha-automation-action";
import type HaAutomationAction from "../automation/action/ha-automation-action";
import "../automation/ha-automation-sidebar";
import { showPasteReplaceDialog } from "../automation/paste-replace-dialog/show-dialog-paste-replace";
import { saveFabStyles } from "../automation/styles";
import { manualEditorStyles, saveFabStyles } from "../automation/styles";
import "./ha-script-fields";
import type HaScriptFields from "./ha-script-fields";
@@ -194,7 +194,12 @@ export class HaManualScriptEditor extends LitElement {
protected render() {
return html`
<div class="split-view">
<div
class=${classMap({
"split-view": true,
"sidebar-hidden": !this._sidebarConfig,
})}
>
<div class="content-wrapper">
<div class="content">${this._renderContent()}</div>
<ha-fab
@@ -211,7 +216,6 @@ export class HaManualScriptEditor extends LitElement {
<ha-automation-sidebar
class=${classMap({
sidebar: true,
hidden: !this._sidebarConfig,
overlay: !this.isWide,
rtl: computeRTL(this.hass),
})}
@@ -504,79 +508,8 @@ export class HaManualScriptEditor extends LitElement {
static get styles(): CSSResultGroup {
return [
saveFabStyles,
manualEditorStyles,
css`
:host {
display: block;
}
.split-view {
display: flex;
flex-direction: row;
height: 100%;
position: relative;
gap: 16px;
}
.content-wrapper {
position: relative;
flex: 6;
}
.content {
padding: 32px 16px 64px 0;
height: calc(100vh - 153px);
height: calc(100dvh - 153px);
overflow-y: auto;
overflow-x: hidden;
}
.sidebar {
padding: 12px 0;
flex: 4;
height: calc(100vh - 81px);
height: calc(100dvh - 81px);
width: 40%;
}
.sidebar.hidden {
border-color: transparent;
border-width: 0;
overflow: hidden;
flex: 0;
visibility: hidden;
}
.sidebar.overlay {
position: fixed;
bottom: 0;
right: 0;
height: calc(100% - 64px);
padding: 0;
z-index: 5;
}
.sidebar.overlay.rtl {
right: unset;
left: 8px;
}
@media all and (max-width: 870px) {
.split-view {
gap: 0;
margin-right: -8px;
}
.sidebar {
height: 0;
width: 0;
flex: 0;
}
}
.sidebar.overlay.hidden {
width: 0;
}
.description {
margin: 0;
}
.header {
display: flex;
align-items: center;
@@ -589,9 +522,6 @@ export class HaManualScriptEditor extends LitElement {
font-weight: var(--ha-font-weight-normal);
flex: 1;
}
.header a {
color: var(--secondary-text-color);
}
`,
];
}

View File

@@ -4472,7 +4472,9 @@
"no_conditions": "[%key:ui::panel::config::devices::automation::conditions::no_conditions%]",
"sequence": "Actions",
"option_label": "Option",
"default_option_label": "Default actions",
"option_description": "Choose actions based on conditions",
"default_option_description": "Optional actions to run if no other options match",
"description": {
"picker": "Choose what to do based on conditions (Similar to If-then, but more powerful).",
"full": "Choose {number, plural,\n one {an option}\n other{between {number} options}\n}",