From 3a03abbd3ad9ac5768c4135d61b8a4eba6aa987c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 28 Sep 2023 17:48:57 +0200 Subject: [PATCH] Add "Add condition" button in the editor --- .../lovelace/common/validate-condition.ts | 8 +- .../conditions}/ha-card-condition-editor.ts | 28 +++--- .../lovelace/editor/conditions/types.ts | 6 ++ .../types/ha-card-condition-responsive.ts | 16 ++-- .../types/ha-card-condition-state.ts | 16 ++-- .../hui-conditional-card-editor.ts | 85 ++++++++++++++----- src/translations/en.json | 1 + 7 files changed, 111 insertions(+), 49 deletions(-) rename src/panels/lovelace/{common => editor/conditions}/ha-card-condition-editor.ts (79%) create mode 100644 src/panels/lovelace/editor/conditions/types.ts rename src/panels/lovelace/{common => editor/conditions}/types/ha-card-condition-responsive.ts (90%) rename src/panels/lovelace/{common => editor/conditions}/types/ha-card-condition-state.ts (88%) diff --git a/src/panels/lovelace/common/validate-condition.ts b/src/panels/lovelace/common/validate-condition.ts index 8ecc85ac25..57d4317662 100644 --- a/src/panels/lovelace/common/validate-condition.ts +++ b/src/panels/lovelace/common/validate-condition.ts @@ -3,6 +3,12 @@ import { HomeAssistant } from "../../../types"; export type Condition = StateCondition | ResponsiveCondition; +export type LegacyCondition = { + entity?: string; + state?: string; + state_not?: string; +}; + export type StateCondition = { condition: "state"; entity?: string; @@ -50,7 +56,7 @@ export function checkConditionsMet( function valideStateCondition(condition: StateCondition) { return ( - !!condition.entity && + condition.entity != null && (condition.state != null || condition.state_not != null) ); } diff --git a/src/panels/lovelace/common/ha-card-condition-editor.ts b/src/panels/lovelace/editor/conditions/ha-card-condition-editor.ts similarity index 79% rename from src/panels/lovelace/common/ha-card-condition-editor.ts rename to src/panels/lovelace/editor/conditions/ha-card-condition-editor.ts index 8dea90a462..0e66d73a17 100644 --- a/src/panels/lovelace/common/ha-card-condition-editor.ts +++ b/src/panels/lovelace/editor/conditions/ha-card-condition-editor.ts @@ -1,34 +1,36 @@ import { mdiCodeBraces, mdiDelete, mdiListBoxOutline } from "@mdi/js"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { dynamicElement } from "../../../common/dom/dynamic-element-directive"; -import { fireEvent } from "../../../common/dom/fire_event"; -import "../../../components/ha-icon-button"; -import "../../../components/ha-yaml-editor"; -import { haStyle } from "../../../resources/styles"; -import type { HomeAssistant } from "../../../types"; -import "./types/ha-card-condition-responsive"; -import "./types/ha-card-condition-state"; -import { Condition } from "./validate-condition"; +import { dynamicElement } from "../../../../common/dom/dynamic-element-directive"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-icon-button"; +import "../../../../components/ha-yaml-editor"; +import { haStyle } from "../../../../resources/styles"; +import type { HomeAssistant } from "../../../../types"; +import { Condition, LegacyCondition } from "../../common/validate-condition"; +import type { LovelaceConditionEditorConstructor } from "./types"; @customElement("ha-card-condition-editor") export default class HaCardConditionEditor extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) condition!: Condition; + @property({ attribute: false }) condition!: Condition | LegacyCondition; @state() public _yamlMode = false; protected render() { - const condition = this.condition; + const condition: Condition = { + condition: "state", + ...this.condition, + }; const element = customElements.get( `ha-card-condition-${condition.condition}` - ) as any | undefined; + ) as LovelaceConditionEditorConstructor | undefined; const supported = element !== undefined; const valid = element && - (!element.validateUIConfig || element.validateUIConfig(this.condition)); + (!element.validateUIConfig || element.validateUIConfig(condition)); const yamlMode = this._yamlMode || !supported || !valid; diff --git a/src/panels/lovelace/editor/conditions/types.ts b/src/panels/lovelace/editor/conditions/types.ts new file mode 100644 index 0000000000..cdfe8a6c8f --- /dev/null +++ b/src/panels/lovelace/editor/conditions/types.ts @@ -0,0 +1,6 @@ +import { Condition } from "../../common/validate-condition"; + +export interface LovelaceConditionEditorConstructor { + defaultConfig?: Condition; + validateUIConfig?: (condition: Condition) => boolean; +} diff --git a/src/panels/lovelace/common/types/ha-card-condition-responsive.ts b/src/panels/lovelace/editor/conditions/types/ha-card-condition-responsive.ts similarity index 90% rename from src/panels/lovelace/common/types/ha-card-condition-responsive.ts rename to src/panels/lovelace/editor/conditions/types/ha-card-condition-responsive.ts index e6b422456c..7d73baf7ce 100644 --- a/src/panels/lovelace/common/types/ha-card-condition-responsive.ts +++ b/src/panels/lovelace/editor/conditions/types/ha-card-condition-responsive.ts @@ -1,14 +1,14 @@ import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; -import { getAllCombinations } from "../../../../common/array/combinations"; -import { fireEvent } from "../../../../common/dom/fire_event"; -import { LocalizeFunc } from "../../../../common/translations/localize"; -import "../../../../components/ha-form/ha-form"; -import type { SchemaUnion } from "../../../../components/ha-form/types"; -import { HaFormSchema } from "../../../../components/ha-form/types"; -import type { HomeAssistant } from "../../../../types"; -import { ResponsiveCondition } from "../validate-condition"; +import { getAllCombinations } from "../../../../../common/array/combinations"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { LocalizeFunc } from "../../../../../common/translations/localize"; +import "../../../../../components/ha-form/ha-form"; +import type { SchemaUnion } from "../../../../../components/ha-form/types"; +import { HaFormSchema } from "../../../../../components/ha-form/types"; +import type { HomeAssistant } from "../../../../../types"; +import { ResponsiveCondition } from "../../../common/validate-condition"; const BREAKPOINT_VALUES = [0, 768, 1024, 1280, Infinity]; const BREAKPOINTS = ["mobile", "tablet", "desktop", "wide"] as const; diff --git a/src/panels/lovelace/common/types/ha-card-condition-state.ts b/src/panels/lovelace/editor/conditions/types/ha-card-condition-state.ts similarity index 88% rename from src/panels/lovelace/common/types/ha-card-condition-state.ts rename to src/panels/lovelace/editor/conditions/types/ha-card-condition-state.ts index 87ac4ab768..f550847aa3 100644 --- a/src/panels/lovelace/common/types/ha-card-condition-state.ts +++ b/src/panels/lovelace/editor/conditions/types/ha-card-condition-state.ts @@ -2,13 +2,13 @@ import { html, LitElement, PropertyValues } from "lit"; import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; import { assert, literal, object, optional, string } from "superstruct"; -import { fireEvent } from "../../../../common/dom/fire_event"; -import { LocalizeFunc } from "../../../../common/translations/localize"; -import "../../../../components/ha-form/ha-form"; -import type { SchemaUnion } from "../../../../components/ha-form/types"; -import { HaFormSchema } from "../../../../components/ha-form/types"; -import type { HomeAssistant } from "../../../../types"; -import { StateCondition } from "../validate-condition"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { LocalizeFunc } from "../../../../../common/translations/localize"; +import "../../../../../components/ha-form/ha-form"; +import type { SchemaUnion } from "../../../../../components/ha-form/types"; +import { HaFormSchema } from "../../../../../components/ha-form/types"; +import type { HomeAssistant } from "../../../../../types"; +import { StateCondition } from "../../../common/validate-condition"; const stateConditionStruct = object({ condition: literal("state"), @@ -33,7 +33,7 @@ export class HaCardConditionState extends LitElement { @property({ type: Boolean }) public disabled = false; public static get defaultConfig(): StateCondition { - return { condition: "state", entity: "" }; + return { condition: "state", entity: "", state: "" }; } protected willUpdate(changedProperties: PropertyValues): void { diff --git a/src/panels/lovelace/editor/config-elements/hui-conditional-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-conditional-card-editor.ts index c9a6e2673e..ae7abda10b 100644 --- a/src/panels/lovelace/editor/config-elements/hui-conditional-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-conditional-card-editor.ts @@ -1,31 +1,48 @@ import "@material/mwc-tab-bar/mwc-tab-bar"; import "@material/mwc-tab/mwc-tab"; import type { MDCTabBarActivatedEvent } from "@material/tab-bar"; -import { mdiCodeBraces, mdiContentCopy, mdiListBoxOutline } from "@mdi/js"; +import { + mdiCodeBraces, + mdiContentCopy, + mdiContentDuplicate, + mdiListBoxOutline, + mdiPlus, +} from "@mdi/js"; import deepClone from "deep-clone-simple"; import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { any, array, assert, assign, object, optional } from "superstruct"; import { storage } from "../../../../common/decorators/storage"; import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/entity/ha-entity-picker"; +import { stopPropagation } from "../../../../common/dom/stop_propagation"; +import "../../../../components/ha-button"; +import "../../../../components/ha-list-item"; +import "../../../../components/ha-menu-button"; +import type { HaSelect } from "../../../../components/ha-select"; +import "../../../../components/ha-svg-icon"; import type { LovelaceCardConfig, LovelaceConfig, } from "../../../../data/lovelace"; import type { HomeAssistant } from "../../../../types"; import type { ConditionalCardConfig } from "../../cards/types"; -import "../../common/ha-card-condition-editor"; +import { Condition } from "../../common/validate-condition"; import type { LovelaceCardEditor } from "../../types"; import "../card-editor/hui-card-element-editor"; import type { HuiCardElementEditor } from "../card-editor/hui-card-element-editor"; import "../card-editor/hui-card-picker"; +import "../conditions/ha-card-condition-editor"; +import "../conditions/types/ha-card-condition-responsive"; +import "../conditions/types/ha-card-condition-state"; +import { LovelaceConditionEditorConstructor } from "../conditions/types"; import "../hui-element-editor"; import type { ConfigChangedEvent } from "../hui-element-editor"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import type { GUIModeChangedEvent } from "../types"; import { configElementStyle } from "./config-elements-style"; +const UI_CONDITION: Condition["condition"][] = ["state", "responsive"]; + const cardConfigStruct = assign( baseLovelaceCardConfig, object({ @@ -158,13 +175,35 @@ export class HuiConditionalCardEditor ` )} -
-
- -
+
+ + + + + ${UI_CONDITION.map( + (condition) => html` + + ${this.hass!.localize( + `ui.panel.lovelace.editor.card.conditional.condition.${condition}.label` + ) || condition} + + + ` + )} +
`} @@ -233,19 +272,24 @@ export class HuiConditionalCardEditor fireEvent(this, "config-changed", { config: this._config }); } - private _addCondition(ev: Event): void { - const target = ev.target! as any; - if (target.value === "" || !this._config) { + private _addCondition(ev: CustomEvent): void { + const condition = (ev.currentTarget as HaSelect).items[ev.detail.index] + .value as Condition["condition"]; + if (!this._config) { return; } const conditions = [...this._config.conditions]; - conditions.push({ - condition: "state", - entity: target.value, - state: "", - }); + + const elClass = customElements.get(`ha-card-condition-${condition}`) as + | LovelaceConditionEditorConstructor + | undefined; + + conditions.push( + elClass?.defaultConfig + ? { ...elClass.defaultConfig } + : { condition: condition } + ); this._config = { ...this._config, conditions }; - target.value = ""; fireEvent(this, "config-changed", { config: this._config }); } @@ -282,6 +326,9 @@ export class HuiConditionalCardEditor .condition .content { padding: 12px; } + ha-button-menu { + margin-top: 12px; + } .card { margin-top: 8px; border: 1px solid var(--divider-color); diff --git a/src/translations/en.json b/src/translations/en.json index a4039e1e4b..b8bfd3f096 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4769,6 +4769,7 @@ "current_state": "current", "condition_explanation": "The card will be shown when ALL conditions below are fulfilled.", "change_type": "Change type", + "add_condition": "Add condition", "condition": { "responsive": { "label": "Responsive",