From 4ccc5f02effcee0d4712f93ce1a209ab5a657663 Mon Sep 17 00:00:00 2001 From: karwosts Date: Tue, 13 Feb 2024 09:20:56 -0800 Subject: [PATCH 1/8] Deduplicate blueprint editor code --- .../automation/blueprint-automation-editor.ts | 259 +--------------- .../blueprint/blueprint-generic-editor.ts | 276 ++++++++++++++++++ .../config/script/blueprint-script-editor.ts | 258 +--------------- 3 files changed, 294 insertions(+), 499 deletions(-) create mode 100644 src/panels/config/blueprint/blueprint-generic-editor.ts diff --git a/src/panels/config/automation/blueprint-automation-editor.ts b/src/panels/config/automation/blueprint-automation-editor.ts index f57806f89613..f28d27a27d2b 100644 --- a/src/panels/config/automation/blueprint-automation-editor.ts +++ b/src/panels/config/automation/blueprint-automation-editor.ts @@ -1,9 +1,7 @@ import "@material/mwc-button/mwc-button"; import { HassEntity } from "home-assistant-js-websocket"; -import { css, CSSResultGroup, html, LitElement } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { fireEvent } from "../../../common/dom/fire_event"; -import { nestedArrayMove } from "../../../common/util/array-move"; +import { html } from "lit"; +import { customElement, property } from "lit/decorators"; import "../../../components/ha-alert"; import "../../../components/ha-blueprint-picker"; import "../../../components/ha-card"; @@ -12,45 +10,21 @@ import "../../../components/ha-markdown"; import "../../../components/ha-selector/ha-selector"; import "../../../components/ha-settings-row"; import { BlueprintAutomationConfig } from "../../../data/automation"; -import { - BlueprintOrError, - Blueprints, - fetchBlueprints, -} from "../../../data/blueprint"; -import { haStyle } from "../../../resources/styles"; -import { HomeAssistant } from "../../../types"; +import { fetchBlueprints } from "../../../data/blueprint"; +import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor"; import "../ha-config-section"; @customElement("blueprint-automation-editor") -export class HaBlueprintAutomationEditor extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property({ type: Boolean }) public isWide = false; - - @property({ type: Boolean }) public disabled = false; - - @property({ type: Boolean, reflect: true }) public narrow = false; - +export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor { @property({ attribute: false }) public config!: BlueprintAutomationConfig; @property({ attribute: false }) public stateObj?: HassEntity; - @state() private _blueprints?: Blueprints; - - protected firstUpdated(changedProps) { - super.firstUpdated(changedProps); - this._getBlueprints(); - } - - private get _blueprint(): BlueprintOrError | undefined { - if (!this._blueprints) { - return undefined; - } - return this._blueprints[this.config.use_blueprint.path]; + protected get _config(): BlueprintAutomationConfig { + return this.config; } protected render() { - const blueprint = this._blueprint; return html` ${this.disabled ? html` @@ -77,167 +51,14 @@ export class HaBlueprintAutomationEditor extends LitElement { ${this.config.description ? html`

${this.config.description}

` : ""} - -
- ${this._blueprints - ? Object.keys(this._blueprints).length - ? html` - - ` - : this.hass.localize( - "ui.panel.config.automation.editor.blueprint.no_blueprints" - ) - : html``} -
- - ${this.config.use_blueprint.path - ? blueprint && "error" in blueprint - ? html`

- There is an error in this Blueprint: ${blueprint.error} -

` - : html`${blueprint?.metadata.description - ? html`` - : ""} - ${blueprint?.metadata?.input && - Object.keys(blueprint.metadata.input).length - ? Object.entries(blueprint.metadata.input).map( - ([key, value]) => { - const selector = value?.selector ?? { text: undefined }; - const type = Object.keys(selector)[0]; - const enhancedSelector = [ - "action", - "condition", - "trigger", - ].includes(type) - ? { - [type]: { - ...selector[type], - path: [key], - }, - } - : selector; - - return html` - ${value?.name || key} - - ${html``} - `; - } - ) - : html`

- ${this.hass.localize( - "ui.panel.config.automation.editor.blueprint.no_inputs" - )} -

`}` - : ""} -
+ ${this.renderCard()} `; } - private async _getBlueprints() { + protected async _getBlueprints() { this._blueprints = await fetchBlueprints(this.hass, "automation"); } - private _blueprintChanged(ev) { - ev.stopPropagation(); - if (this.config.use_blueprint.path === ev.detail.value) { - return; - } - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - path: ev.detail.value, - }, - }, - }); - } - - private _inputChanged(ev) { - ev.stopPropagation(); - const target = ev.target as any; - const key = target.key; - const value = ev.detail ? ev.detail.value : target.value; - if ( - (this.config.use_blueprint.input && - this.config.use_blueprint.input[key] === value) || - (!this.config.use_blueprint.input && value === "") - ) { - return; - } - const input = { ...this.config.use_blueprint.input, [key]: value }; - - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - ...this.config.use_blueprint, - input, - }, - }, - }); - } - - private _itemMoved(ev) { - ev.stopPropagation(); - const { oldIndex, newIndex, oldPath, newPath } = ev.detail; - - const input = nestedArrayMove( - this.config.use_blueprint.input, - oldIndex, - newIndex, - oldPath, - newPath - ); - - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - ...this.config.use_blueprint, - input, - }, - }, - }); - } - private async _enable(): Promise { if (!this.hass || !this.stateObj) { return; @@ -246,69 +67,7 @@ export class HaBlueprintAutomationEditor extends LitElement { entity_id: this.stateObj.entity_id, }); } - - private _duplicate() { - fireEvent(this, "duplicate"); - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - :host { - display: block; - } - ha-card.blueprint { - margin: 0 auto; - } - .padding { - padding: 16px; - } - .link-button-row { - padding: 14px; - } - .blueprint-picker-container { - padding: 0 16px 16px; - } - ha-textfield, - ha-blueprint-picker { - display: block; - } - h3 { - margin: 16px; - } - .introduction { - margin-top: 0; - margin-bottom: 12px; - } - .introduction a { - color: var(--primary-color); - } - p { - margin-bottom: 0; - } - .description { - margin-bottom: 16px; - } - ha-settings-row { - --paper-time-input-justify-content: flex-end; - --settings-row-content-width: 100%; - --settings-row-prefix-display: contents; - border-top: 1px solid var(--divider-color); - } - ha-alert { - margin-bottom: 16px; - display: block; - } - ha-alert.re-order { - border-radius: var(--ha-card-border-radius, 12px); - overflow: hidden; - } - `, - ]; - } } - declare global { interface HTMLElementTagNameMap { "blueprint-automation-editor": HaBlueprintAutomationEditor; diff --git a/src/panels/config/blueprint/blueprint-generic-editor.ts b/src/panels/config/blueprint/blueprint-generic-editor.ts new file mode 100644 index 000000000000..c11ab069122e --- /dev/null +++ b/src/panels/config/blueprint/blueprint-generic-editor.ts @@ -0,0 +1,276 @@ +import "@material/mwc-button/mwc-button"; +import { css, CSSResultGroup, html, LitElement } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { nestedArrayMove } from "../../../common/util/array-move"; +import "../../../components/ha-alert"; +import "../../../components/ha-blueprint-picker"; +import "../../../components/ha-card"; +import "../../../components/ha-circular-progress"; +import "../../../components/ha-markdown"; +import "../../../components/ha-selector/ha-selector"; +import "../../../components/ha-settings-row"; +import { BlueprintAutomationConfig } from "../../../data/automation"; +import { BlueprintOrError, Blueprints } from "../../../data/blueprint"; +import { BlueprintScriptConfig } from "../../../data/script"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import "../ha-config-section"; + +@customElement("blueprint-generic-editor") +export abstract class HaBlueprintGenericEditor extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean }) public isWide = false; + + @property({ type: Boolean }) public disabled = false; + + @property({ type: Boolean, reflect: true }) public narrow = false; + + @state() protected _blueprints?: Blueprints; + + protected firstUpdated(changedProps) { + super.firstUpdated(changedProps); + this._getBlueprints(); + } + + protected get _blueprint(): BlueprintOrError | undefined { + if (!this._blueprints) { + return undefined; + } + return this._blueprints[this._config.use_blueprint.path]; + } + + protected abstract get _config(): + | BlueprintAutomationConfig + | BlueprintScriptConfig; + + protected renderCard() { + const blueprint = this._blueprint; + return html` + +
+ ${this._blueprints + ? Object.keys(this._blueprints).length + ? html` + + ` + : this.hass.localize( + "ui.panel.config.automation.editor.blueprint.no_blueprints" + ) + : html``} +
+ + ${this._config.use_blueprint.path + ? blueprint && "error" in blueprint + ? html`

+ There is an error in this Blueprint: ${blueprint.error} +

` + : html`${blueprint?.metadata.description + ? html`` + : ""} + ${blueprint?.metadata?.input && + Object.keys(blueprint.metadata.input).length + ? Object.entries(blueprint.metadata.input).map( + ([key, value]) => { + const selector = value?.selector ?? { text: undefined }; + const type = Object.keys(selector)[0]; + const enhancedSelector = [ + "action", + "condition", + "trigger", + ].includes(type) + ? { + [type]: { + ...selector[type], + path: [key], + }, + } + : selector; + + return html` + ${value?.name || key} + + ${html``} + `; + } + ) + : html`

+ ${this.hass.localize( + "ui.panel.config.automation.editor.blueprint.no_inputs" + )} +

`}` + : ""} +
+ `; + } + + protected abstract _getBlueprints(); + + private _blueprintChanged(ev) { + ev.stopPropagation(); + if (this._config.use_blueprint.path === ev.detail.value) { + return; + } + fireEvent(this, "value-changed", { + value: { + ...this._config, + use_blueprint: { + path: ev.detail.value, + }, + }, + }); + } + + private _inputChanged(ev) { + ev.stopPropagation(); + const target = ev.target as any; + const key = target.key; + const value = ev.detail ? ev.detail.value : target.value; + if ( + (this._config.use_blueprint.input && + this._config.use_blueprint.input[key] === value) || + (!this._config.use_blueprint.input && value === "") + ) { + return; + } + const input = { ...this._config.use_blueprint.input, [key]: value }; + + fireEvent(this, "value-changed", { + value: { + ...this._config, + use_blueprint: { + ...this._config.use_blueprint, + input, + }, + }, + }); + } + + private _itemMoved(ev) { + ev.stopPropagation(); + const { oldIndex, newIndex, oldPath, newPath } = ev.detail; + + const input = nestedArrayMove( + this._config.use_blueprint.input, + oldIndex, + newIndex, + oldPath, + newPath + ); + + fireEvent(this, "value-changed", { + value: { + ...this._config, + use_blueprint: { + ...this._config.use_blueprint, + input, + }, + }, + }); + } + + protected _duplicate() { + fireEvent(this, "duplicate"); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + :host { + display: block; + } + ha-card.blueprint { + margin: 0 auto; + } + .padding { + padding: 16px; + } + .link-button-row { + padding: 14px; + } + .blueprint-picker-container { + padding: 0 16px 16px; + } + ha-textfield, + ha-blueprint-picker { + display: block; + } + h3 { + margin: 16px; + } + .introduction { + margin-top: 0; + margin-bottom: 12px; + } + .introduction a { + color: var(--primary-color); + } + p { + margin-bottom: 0; + } + .description { + margin-bottom: 16px; + } + ha-settings-row { + --paper-time-input-justify-content: flex-end; + --settings-row-content-width: 100%; + --settings-row-prefix-display: contents; + border-top: 1px solid var(--divider-color); + } + ha-alert { + margin-bottom: 16px; + display: block; + } + ha-alert.re-order { + border-radius: var(--ha-card-border-radius, 12px); + overflow: hidden; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "blueprint-generic-editor": HaBlueprintGenericEditor; + } +} diff --git a/src/panels/config/script/blueprint-script-editor.ts b/src/panels/config/script/blueprint-script-editor.ts index be455965f4cc..49818d724273 100644 --- a/src/panels/config/script/blueprint-script-editor.ts +++ b/src/panels/config/script/blueprint-script-editor.ts @@ -1,7 +1,5 @@ -import { css, CSSResultGroup, html, LitElement } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { fireEvent } from "../../../common/dom/fire_event"; -import { nestedArrayMove } from "../../../common/util/array-move"; +import { html } from "lit"; +import { customElement, property } from "lit/decorators"; import "../../../components/ha-alert"; import "../../../components/ha-blueprint-picker"; import "../../../components/ha-card"; @@ -9,44 +7,20 @@ import "../../../components/ha-circular-progress"; import "../../../components/ha-markdown"; import "../../../components/ha-selector/ha-selector"; import "../../../components/ha-settings-row"; -import { - BlueprintOrError, - Blueprints, - fetchBlueprints, -} from "../../../data/blueprint"; import { BlueprintScriptConfig } from "../../../data/script"; -import { haStyle } from "../../../resources/styles"; -import { HomeAssistant } from "../../../types"; +import { fetchBlueprints } from "../../../data/blueprint"; +import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor"; import "../ha-config-section"; @customElement("blueprint-script-editor") -export class HaBlueprintScriptEditor extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property({ type: Boolean }) public isWide = false; - - @property({ reflect: true, type: Boolean }) public narrow = false; - - @property({ type: Boolean }) public disabled = false; - +export class HaBlueprintScriptEditor extends HaBlueprintGenericEditor { @property({ attribute: false }) public config!: BlueprintScriptConfig; - @state() private _blueprints?: Blueprints; - - protected firstUpdated(changedProps) { - super.firstUpdated(changedProps); - this._getBlueprints(); - } - - private get _blueprint(): BlueprintOrError | undefined { - if (!this._blueprints) { - return undefined; - } - return this._blueprints[this.config.use_blueprint.path]; + protected get _config(): BlueprintScriptConfig { + return this.config; } protected render() { - const blueprint = this._blueprint; return html` ${this.disabled ? html` @@ -56,228 +30,14 @@ export class HaBlueprintScriptEditor extends LitElement { ` : ""} - -
- ${this._blueprints - ? Object.keys(this._blueprints).length - ? html` - - ` - : this.hass.localize( - "ui.panel.config.automation.editor.blueprint.no_blueprints" - ) - : html``} -
- ${this.config.use_blueprint.path - ? blueprint && "error" in blueprint - ? html`

- There is an error in this Blueprint: ${blueprint.error} -

` - : html`${blueprint?.metadata.description - ? html`` - : ""} - ${blueprint?.metadata?.input && - Object.keys(blueprint.metadata.input).length - ? Object.entries(blueprint.metadata.input).map( - ([key, value]) => { - const selector = value?.selector ?? { text: undefined }; - const type = Object.keys(selector)[0]; - const enhancedSelector = [ - "action", - "condition", - "trigger", - ].includes(type) - ? { - [type]: { - ...selector[type], - path: [key], - }, - } - : selector; - - return html` - ${value?.name || key} - - ${html``} - `; - } - ) - : html`

- ${this.hass.localize( - "ui.panel.config.automation.editor.blueprint.no_inputs" - )} -

`}` - : ""} -
+ ${this.renderCard()} `; } - private async _getBlueprints() { + protected async _getBlueprints() { this._blueprints = await fetchBlueprints(this.hass, "script"); } - - private _blueprintChanged(ev) { - ev.stopPropagation(); - if (this.config.use_blueprint.path === ev.detail.value) { - return; - } - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - path: ev.detail.value, - }, - }, - }); - } - - private _inputChanged(ev) { - ev.stopPropagation(); - const target = ev.target as any; - const key = target.key; - const value = ev.detail ? ev.detail.value : target.value; - if ( - (this.config.use_blueprint.input && - this.config.use_blueprint.input[key] === value) || - (!this.config.use_blueprint.input && value === "") - ) { - return; - } - const input = { ...this.config.use_blueprint.input, [key]: value }; - - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - ...this.config.use_blueprint, - input, - }, - }, - }); - } - - private _itemMoved(ev) { - ev.stopPropagation(); - const { oldIndex, newIndex, oldPath, newPath } = ev.detail; - - const input = nestedArrayMove( - this.config.use_blueprint.input, - oldIndex, - newIndex, - oldPath, - newPath - ); - - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - ...this.config.use_blueprint, - input, - }, - }, - }); - } - - private _duplicate() { - fireEvent(this, "duplicate"); - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - :host { - display: block; - } - ha-card.blueprint { - margin: 0 auto; - } - .padding { - padding: 16px; - } - .link-button-row { - padding: 14px; - } - .blueprint-picker-container { - padding: 0 16px 16px; - } - ha-textfield, - ha-blueprint-picker { - display: block; - } - h3 { - margin: 16px; - } - .introduction { - margin-top: 0; - margin-bottom: 12px; - } - .introduction a { - color: var(--primary-color); - } - p { - margin-bottom: 0; - } - .description { - margin-bottom: 16px; - } - ha-settings-row { - --paper-time-input-justify-content: flex-end; - --settings-row-content-width: 100%; - --settings-row-prefix-display: contents; - border-top: 1px solid var(--divider-color); - } - ha-alert { - margin-bottom: 16px; - display: block; - } - ha-alert.re-order { - border-radius: var(--ha-card-border-radius, 12px); - overflow: hidden; - } - `, - ]; - } } - declare global { interface HTMLElementTagNameMap { "blueprint-script-editor": HaBlueprintScriptEditor; From a0bbe6eb90e07f67519477c1d57b7ec3bed5a616 Mon Sep 17 00:00:00 2001 From: karwosts Date: Tue, 13 Feb 2024 17:02:38 -0800 Subject: [PATCH 2/8] Collapsible blueprint sections --- src/data/blueprint.ts | 8 + .../blueprint/blueprint-generic-editor.ts | 143 +++++++++++++----- 2 files changed, 111 insertions(+), 40 deletions(-) diff --git a/src/data/blueprint.ts b/src/data/blueprint.ts index f86f5743c3e8..7903c1492f91 100644 --- a/src/data/blueprint.ts +++ b/src/data/blueprint.ts @@ -14,6 +14,7 @@ export interface BlueprintMetaData { domain: BlueprintDomain; name: string; input?: Record; + input_sections?: Record; description?: string; source_url?: string; author?: string; @@ -24,6 +25,13 @@ export interface BlueprintInput { description?: string; selector?: Selector; default?: any; + section?: string; +} + +export interface BlueprintInputSection { + name: string; + icon?: string; + default_collapsed?: boolean; } export interface BlueprintImportResult { diff --git a/src/panels/config/blueprint/blueprint-generic-editor.ts b/src/panels/config/blueprint/blueprint-generic-editor.ts index c11ab069122e..7029d2abb672 100644 --- a/src/panels/config/blueprint/blueprint-generic-editor.ts +++ b/src/panels/config/blueprint/blueprint-generic-editor.ts @@ -1,5 +1,5 @@ import "@material/mwc-button/mwc-button"; -import { css, CSSResultGroup, html, LitElement } from "lit"; +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../common/dom/fire_event"; import { nestedArrayMove } from "../../../common/util/array-move"; @@ -11,7 +11,11 @@ import "../../../components/ha-markdown"; import "../../../components/ha-selector/ha-selector"; import "../../../components/ha-settings-row"; import { BlueprintAutomationConfig } from "../../../data/automation"; -import { BlueprintOrError, Blueprints } from "../../../data/blueprint"; +import { + Blueprint, + BlueprintOrError, + Blueprints, +} from "../../../data/blueprint"; import { BlueprintScriptConfig } from "../../../data/script"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; @@ -47,6 +51,8 @@ export abstract class HaBlueprintGenericEditor extends LitElement { protected renderCard() { const blueprint = this._blueprint; + const renderedSections = new Set(); + let border = true; return html` { - const selector = value?.selector ?? { text: undefined }; - const type = Object.keys(selector)[0]; - const enhancedSelector = [ - "action", - "condition", - "trigger", - ].includes(type) - ? { - [type]: { - ...selector[type], - path: [key], - }, - } - : selector; - - return html` - ${value?.name || key} - - ${html``} - `; + if (value?.section) { + key = value.section; + if (!renderedSections.has(key)) { + renderedSections.add(key); + border = false; + return this.renderSection(key); + } + return nothing; + } + const row = this.renderSettingRow(key, value, border); + border = true; + return row; } ) : html`

@@ -142,6 +122,75 @@ export abstract class HaBlueprintGenericEditor extends LitElement { `; } + private renderSection(sectionKey: string) { + const blueprint = this._blueprint as Blueprint; + const section = blueprint?.metadata?.input_sections?.[sectionKey]; + const title = section?.name || sectionKey; + const expanded = !section?.default_collapsed; + + return html` +

+ ${section?.icon + ? html` ` + : nothing} + +
+
+ ${blueprint?.metadata?.input && + Object.keys(blueprint?.metadata?.input).length + ? Object.entries(blueprint?.metadata?.input).map(([key, value]) => { + if (value?.section === sectionKey) { + return this.renderSettingRow(key, value, true); + } + return nothing; + }) + : nothing} +
+ `; + } + + private renderSettingRow(key: string, value: any, border: boolean) { + const selector = value?.selector ?? { text: undefined }; + const type = Object.keys(selector)[0]; + const enhancedSelector = ["action", "condition", "trigger"].includes(type) + ? { + [type]: { + ...selector[type], + path: [key], + }, + } + : selector; + return html` + ${value?.name || key} + + ${html``} + `; + } + protected abstract _getBlueprints(); private _blueprintChanged(ev) { @@ -254,8 +303,15 @@ export abstract class HaBlueprintGenericEditor extends LitElement { --paper-time-input-justify-content: flex-end; --settings-row-content-width: 100%; --settings-row-prefix-display: contents; + } + ha-settings-row.border { border-top: 1px solid var(--divider-color); } + ha-expansion-panel { + margin: 8px; + margin-left: 8px; + margin-right: 8px; + } ha-alert { margin-bottom: 16px; display: block; @@ -264,6 +320,13 @@ export abstract class HaBlueprintGenericEditor extends LitElement { border-radius: var(--ha-card-border-radius, 12px); overflow: hidden; } + div.section-header { + display: flex; + vertical-align: middle; + } + ha-icon.section-header { + padding-right: 10px; + } `, ]; } From 492fab3033f95c81df9fe00753f2a5fa368824a5 Mon Sep 17 00:00:00 2001 From: karwosts Date: Tue, 13 Feb 2024 17:44:46 -0800 Subject: [PATCH 3/8] add description --- src/panels/config/blueprint/blueprint-generic-editor.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/panels/config/blueprint/blueprint-generic-editor.ts b/src/panels/config/blueprint/blueprint-generic-editor.ts index 7029d2abb672..6e8beb3fca2f 100644 --- a/src/panels/config/blueprint/blueprint-generic-editor.ts +++ b/src/panels/config/blueprint/blueprint-generic-editor.ts @@ -139,6 +139,9 @@ export abstract class HaBlueprintGenericEditor extends LitElement {
+ ${section?.description + ? html`` + : nothing} ${blueprint?.metadata?.input && Object.keys(blueprint?.metadata?.input).length ? Object.entries(blueprint?.metadata?.input).map(([key, value]) => { From b9b01942464cb457787e2ea62768a4438b185634 Mon Sep 17 00:00:00 2001 From: karwosts Date: Fri, 1 Mar 2024 14:51:44 -0800 Subject: [PATCH 4/8] renamed collapsed --- src/data/blueprint.ts | 5 +++-- src/panels/config/blueprint/blueprint-generic-editor.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/data/blueprint.ts b/src/data/blueprint.ts index 7903c1492f91..8e078460f1ee 100644 --- a/src/data/blueprint.ts +++ b/src/data/blueprint.ts @@ -29,9 +29,10 @@ export interface BlueprintInput { } export interface BlueprintInputSection { - name: string; + name?: string; icon?: string; - default_collapsed?: boolean; + description?: string; + collapsed?: boolean; } export interface BlueprintImportResult { diff --git a/src/panels/config/blueprint/blueprint-generic-editor.ts b/src/panels/config/blueprint/blueprint-generic-editor.ts index 6e8beb3fca2f..ac80d173a866 100644 --- a/src/panels/config/blueprint/blueprint-generic-editor.ts +++ b/src/panels/config/blueprint/blueprint-generic-editor.ts @@ -126,7 +126,7 @@ export abstract class HaBlueprintGenericEditor extends LitElement { const blueprint = this._blueprint as Blueprint; const section = blueprint?.metadata?.input_sections?.[sectionKey]; const title = section?.name || sectionKey; - const expanded = !section?.default_collapsed; + const expanded = !section?.collapsed; return html`
From 93ad43acf5ecd5a776abaee6f99cbdf590511d1b Mon Sep 17 00:00:00 2001 From: karwosts Date: Fri, 1 Mar 2024 15:09:34 -0800 Subject: [PATCH 5/8] unused import --- src/panels/config/script/blueprint-script-editor.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/panels/config/script/blueprint-script-editor.ts b/src/panels/config/script/blueprint-script-editor.ts index 33da70c48f60..c233eae66245 100644 --- a/src/panels/config/script/blueprint-script-editor.ts +++ b/src/panels/config/script/blueprint-script-editor.ts @@ -4,7 +4,6 @@ import "../../../components/ha-alert"; import { BlueprintScriptConfig } from "../../../data/script"; import { fetchBlueprints } from "../../../data/blueprint"; import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor"; -import "../ha-config-section"; @customElement("blueprint-script-editor") export class HaBlueprintScriptEditor extends HaBlueprintGenericEditor { From f26beb078463a95305da13b25d660029a6b24c50 Mon Sep 17 00:00:00 2001 From: karwosts Date: Fri, 1 Mar 2024 15:17:43 -0800 Subject: [PATCH 6/8] unused import --- src/panels/config/blueprint/blueprint-generic-editor.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/panels/config/blueprint/blueprint-generic-editor.ts b/src/panels/config/blueprint/blueprint-generic-editor.ts index 7fe153ecccf2..fddcedc9db46 100644 --- a/src/panels/config/blueprint/blueprint-generic-editor.ts +++ b/src/panels/config/blueprint/blueprint-generic-editor.ts @@ -19,7 +19,6 @@ import { import { BlueprintScriptConfig } from "../../../data/script"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import "../ha-config-section"; @customElement("blueprint-generic-editor") export abstract class HaBlueprintGenericEditor extends LitElement { From 2eca8082c0d230b49853d0b689588f197bb3d145 Mon Sep 17 00:00:00 2001 From: karwosts Date: Fri, 1 Mar 2024 21:39:46 -0800 Subject: [PATCH 7/8] Don't allow collapsing sections with required --- src/components/ha-expansion-panel.ts | 18 ++++++-- .../blueprint/blueprint-generic-editor.ts | 44 ++++++++++++++----- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/components/ha-expansion-panel.ts b/src/components/ha-expansion-panel.ts index a148c37f1be3..70433bac1264 100644 --- a/src/components/ha-expansion-panel.ts +++ b/src/components/ha-expansion-panel.ts @@ -21,6 +21,8 @@ export class HaExpansionPanel extends LitElement { @property({ type: Boolean, reflect: true }) leftChevron = false; + @property({ type: Boolean, reflect: true }) noCollapse = false; + @property() header?: string; @property() secondary?: string; @@ -34,16 +36,17 @@ export class HaExpansionPanel extends LitElement {
- ${this.leftChevron + ${this.leftChevron && !this.noCollapse ? html` ${this.secondary}
- ${!this.leftChevron + ${!this.leftChevron && !this.noCollapse ? html` = + {}; + protected firstUpdated(changedProps) { super.firstUpdated(changedProps); this._getBlueprints(); } + protected willUpdate(changedProps) { + if (changedProps.has("config") || changedProps.has("_blueprints")) { + const blueprint = this._blueprint; + this._sections = {}; + if (blueprint && !("error" in blueprint) && blueprint?.metadata?.input) { + Object.entries(blueprint.metadata.input).forEach(([key, value]) => { + if (value?.section) { + const sectionId = value.section; + this._sections[sectionId] = this._sections[sectionId] || []; + this._sections[sectionId].push({ key, input: value }); + } + }); + } + } + } + protected get _blueprint(): BlueprintOrError | undefined { if (!this._blueprints) { return undefined; @@ -125,9 +145,16 @@ export abstract class HaBlueprintGenericEditor extends LitElement { const blueprint = this._blueprint as Blueprint; const section = blueprint?.metadata?.input_sections?.[sectionKey]; const title = section?.name || sectionKey; - const expanded = !section?.collapsed; + const anyRequired = this._sections[sectionKey].some( + (item) => item.input.default === undefined + ); + const expanded = !section?.collapsed || anyRequired; - return html` + return html`
${section?.icon ? html` ` : nothing} - ${blueprint?.metadata?.input && - Object.keys(blueprint?.metadata?.input).length - ? Object.entries(blueprint?.metadata?.input).map(([key, value]) => { - if (value?.section === sectionKey) { - return this.renderSettingRow(key, value, true); - } - return nothing; - }) - : nothing} + ${this._sections[sectionKey].map((item) => + this.renderSettingRow(item.key, item.input, true) + )}
`; } @@ -305,7 +326,6 @@ export abstract class HaBlueprintGenericEditor extends LitElement { --paper-time-input-justify-content: flex-end; --settings-row-content-width: 100%; --settings-row-prefix-display: contents; - border-top: 1px solid var(--divider-color); } ha-settings-row.border { border-top: 1px solid var(--divider-color); From 2a13ae890127abdf206ee524914d1ea54b72dbcf Mon Sep 17 00:00:00 2001 From: karwosts Date: Fri, 17 May 2024 07:00:52 -0700 Subject: [PATCH 8/8] Update to new schema --- src/data/blueprint.ts | 5 +- .../blueprint/blueprint-generic-editor.ts | 63 +++++++------------ 2 files changed, 25 insertions(+), 43 deletions(-) diff --git a/src/data/blueprint.ts b/src/data/blueprint.ts index 8e078460f1ee..222da542af7a 100644 --- a/src/data/blueprint.ts +++ b/src/data/blueprint.ts @@ -13,8 +13,7 @@ export interface Blueprint { export interface BlueprintMetaData { domain: BlueprintDomain; name: string; - input?: Record; - input_sections?: Record; + input?: Record; description?: string; source_url?: string; author?: string; @@ -25,7 +24,6 @@ export interface BlueprintInput { description?: string; selector?: Selector; default?: any; - section?: string; } export interface BlueprintInputSection { @@ -33,6 +31,7 @@ export interface BlueprintInputSection { icon?: string; description?: string; collapsed?: boolean; + input: Record; } export interface BlueprintImportResult { diff --git a/src/panels/config/blueprint/blueprint-generic-editor.ts b/src/panels/config/blueprint/blueprint-generic-editor.ts index 6c76f69a7f46..bae63f6dc7e3 100644 --- a/src/panels/config/blueprint/blueprint-generic-editor.ts +++ b/src/panels/config/blueprint/blueprint-generic-editor.ts @@ -12,8 +12,8 @@ import "../../../components/ha-selector/ha-selector"; import "../../../components/ha-settings-row"; import { BlueprintAutomationConfig } from "../../../data/automation"; import { - Blueprint, BlueprintInput, + BlueprintInputSection, BlueprintOrError, Blueprints, } from "../../../data/blueprint"; @@ -33,30 +33,11 @@ export abstract class HaBlueprintGenericEditor extends LitElement { @state() protected _blueprints?: Blueprints; - private _sections: Record = - {}; - protected firstUpdated(changedProps) { super.firstUpdated(changedProps); this._getBlueprints(); } - protected willUpdate(changedProps) { - if (changedProps.has("config") || changedProps.has("_blueprints")) { - const blueprint = this._blueprint; - this._sections = {}; - if (blueprint && !("error" in blueprint) && blueprint?.metadata?.input) { - Object.entries(blueprint.metadata.input).forEach(([key, value]) => { - if (value?.section) { - const sectionId = value.section; - this._sections[sectionId] = this._sections[sectionId] || []; - this._sections[sectionId].push({ key, input: value }); - } - }); - } - } - } - protected get _blueprint(): BlueprintOrError | undefined { if (!this._blueprints) { return undefined; @@ -70,7 +51,6 @@ export abstract class HaBlueprintGenericEditor extends LitElement { protected renderCard() { const blueprint = this._blueprint; - const renderedSections = new Set(); let border = true; return html` { - if (value?.section) { - key = value.section; - if (!renderedSections.has(key)) { - renderedSections.add(key); - border = false; - return this.renderSection(key); - } - return nothing; + if (value && "input" in value) { + const section = this.renderSection(key, value); + border = false; + return section; } const row = this.renderSettingRow(key, value, border); border = true; @@ -141,14 +117,14 @@ export abstract class HaBlueprintGenericEditor extends LitElement { `; } - private renderSection(sectionKey: string) { - const blueprint = this._blueprint as Blueprint; - const section = blueprint?.metadata?.input_sections?.[sectionKey]; + private renderSection(sectionKey: string, section: BlueprintInputSection) { const title = section?.name || sectionKey; - const anyRequired = this._sections[sectionKey].some( - (item) => item.input.default === undefined - ); - const expanded = !section?.collapsed || anyRequired; + const anyRequired = + section.input && + Object.values(section.input).some( + (item) => item === null || item.default === undefined + ); + const expanded = !section.collapsed || anyRequired; return html` ` : nothing} - ${this._sections[sectionKey].map((item) => - this.renderSettingRow(item.key, item.input, true) - )} + ${section.input + ? Object.entries(section.input).map(([key, value]) => + this.renderSettingRow(key, value, true) + ) + : nothing}
`; } - private renderSettingRow(key: string, value: any, border: boolean) { + private renderSettingRow( + key: string, + value: BlueprintInput | null, + border: boolean + ) { const selector = value?.selector ?? { text: undefined }; const type = Object.keys(selector)[0]; const enhancedSelector = ["action", "condition", "trigger"].includes(type) @@ -292,6 +274,7 @@ export abstract class HaBlueprintGenericEditor extends LitElement { } ha-card.blueprint { margin: 0 auto; + margin-bottom: 64px; } .padding { padding: 16px;