From d7b888f7615f0d735f872fb08979ac4c72c7fea8 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Wed, 17 Aug 2022 23:57:26 -0400 Subject: [PATCH] Fix localize key types related to form schemas (Group 3) (#13400) * Fix key type errors for card editors (Round 4) * Fix key type errors for remaining form schemas --- .../registries/dialog-hassio-registries.ts | 10 +- src/components/ha-form/ha-form.ts | 6 +- .../ha-selector/ha-selector-media.ts | 8 +- src/data/automation.ts | 2 +- src/data/script.ts | 9 +- .../automation/manual-automation-editor.ts | 14 +- .../dialog-lovelace-dashboard-detail.ts | 23 +-- .../dialog-lovelace-resource-detail.ts | 105 +++++++------ src/panels/config/script/ha-script-editor.ts | 116 +++++++------- src/panels/config/zone/dialog-zone-detail.ts | 86 +++++----- .../hui-generic-entity-row-editor.ts | 54 ++++--- .../hui-logbook-card-editor.ts | 34 ++-- .../config-elements/hui-map-card-editor.ts | 24 ++- .../config-elements/hui-sensor-card-editor.ts | 147 +++++++++--------- .../hui-statistics-graph-card-editor.ts | 118 +++++++------- .../hui-weather-forecast-card-editor.ts | 120 +++++++------- .../editor/view-editor/hui-view-editor.ts | 80 +++++----- src/translations/en.json | 4 +- 18 files changed, 503 insertions(+), 457 deletions(-) diff --git a/hassio/src/dialogs/registries/dialog-hassio-registries.ts b/hassio/src/dialogs/registries/dialog-hassio-registries.ts index 7ca0b3de1c..e26753e5dc 100644 --- a/hassio/src/dialogs/registries/dialog-hassio-registries.ts +++ b/hassio/src/dialogs/registries/dialog-hassio-registries.ts @@ -4,7 +4,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { createCloseHeading } from "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-form/ha-form"; -import { HaFormSchema } from "../../../../src/components/ha-form/types"; +import type { SchemaUnion } from "../../../../src/components/ha-form/types"; import "../../../../src/components/ha-icon-button"; import "../../../../src/components/ha-settings-row"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; @@ -19,7 +19,7 @@ import { haStyle, haStyleDialog } from "../../../../src/resources/styles"; import type { HomeAssistant } from "../../../../src/types"; import { RegistriesDialogParams } from "./show-dialog-registries"; -const SCHEMA: HaFormSchema[] = [ +const SCHEMA = [ { name: "registry", required: true, @@ -35,7 +35,7 @@ const SCHEMA: HaFormSchema[] = [ required: true, selector: { text: { type: "password" } }, }, -]; +] as const; @customElement("dialog-hassio-registries") class HassioRegistriesDialog extends LitElement { @@ -135,8 +135,8 @@ class HassioRegistriesDialog extends LitElement { `; } - private _computeLabel = (schema: HaFormSchema) => - this.supervisor.localize(`dialog.registries.${schema.name}`) || schema.name; + private _computeLabel = (schema: SchemaUnion) => + this.supervisor.localize(`dialog.registries.${schema.name}`); private _valueChanged(ev: CustomEvent) { this._input = ev.detail.value; diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts index a42f90d773..7f83b3220c 100644 --- a/src/components/ha-form/ha-form.ts +++ b/src/components/ha-form/ha-form.ts @@ -41,14 +41,14 @@ export class HaForm extends LitElement implements HaFormElement { @property({ type: Boolean }) public disabled = false; - @property() public computeError?: (schema: HaFormSchema, error) => string; + @property() public computeError?: (schema: any, error) => string; @property() public computeLabel?: ( schema: any, - data?: HaFormDataContainer + data: HaFormDataContainer ) => string; - @property() public computeHelper?: (schema: HaFormSchema) => string; + @property() public computeHelper?: (schema: any) => string | undefined; public focus() { const root = this.shadowRoot?.querySelector(".root"); diff --git a/src/components/ha-selector/ha-selector-media.ts b/src/components/ha-selector/ha-selector-media.ts index bd6ad958b8..fc998f5d05 100644 --- a/src/components/ha-selector/ha-selector-media.ts +++ b/src/components/ha-selector/ha-selector-media.ts @@ -15,13 +15,13 @@ import type { HomeAssistant } from "../../types"; import { brandsUrl, extractDomainFromBrandUrl } from "../../util/brands-url"; import "../ha-alert"; import "../ha-form/ha-form"; -import type { HaFormSchema } from "../ha-form/types"; +import type { SchemaUnion } from "../ha-form/types"; import { showMediaBrowserDialog } from "../media-player/show-media-browser-dialog"; const MANUAL_SCHEMA = [ { name: "media_content_id", required: false, selector: { text: {} } }, { name: "media_content_type", required: false, selector: { text: {} } }, -]; +] as const; @customElement("ha-selector-media") export class HaMediaSelector extends LitElement { @@ -163,7 +163,9 @@ export class HaMediaSelector extends LitElement { `}`; } - private _computeLabelCallback = (schema: HaFormSchema): string => + private _computeLabelCallback = ( + schema: SchemaUnion + ): string => this.hass.localize(`ui.components.selectors.media.${schema.name}`); private _entityChanged(ev: CustomEvent) { diff --git a/src/data/automation.ts b/src/data/automation.ts index 3259df2f67..b164df7591 100644 --- a/src/data/automation.ts +++ b/src/data/automation.ts @@ -8,7 +8,7 @@ import { BlueprintInput } from "./blueprint"; import { DeviceCondition, DeviceTrigger } from "./device_automation"; import { Action, MODES } from "./script"; -export const AUTOMATION_DEFAULT_MODE: ManualAutomationConfig["mode"] = "single"; +export const AUTOMATION_DEFAULT_MODE: typeof MODES[number] = "single"; export interface AutomationEntity extends HassEntityBase { attributes: HassEntityAttributeBase & { diff --git a/src/data/script.ts b/src/data/script.ts index 017a72cc88..42c9c849ac 100644 --- a/src/data/script.ts +++ b/src/data/script.ts @@ -28,7 +28,12 @@ import { import { BlueprintInput } from "./blueprint"; export const MODES = ["single", "restart", "queued", "parallel"] as const; -export const MODES_MAX = ["queued", "parallel"]; +export const MODES_MAX = ["queued", "parallel"] as const; + +export const isMaxMode = ( + mode: typeof MODES[number] +): mode is typeof MODES_MAX[number] => + MODES_MAX.includes(mode as typeof MODES_MAX[number]); export const baseActionStruct = object({ alias: optional(string()), @@ -275,7 +280,7 @@ export const canRun = (state: ScriptEntity) => { } if ( state.state === "on" && - MODES_MAX.includes(state.attributes.mode) && + isMaxMode(state.attributes.mode) && state.attributes.current! < state.attributes.max! ) { return true; diff --git a/src/panels/config/automation/manual-automation-editor.ts b/src/panels/config/automation/manual-automation-editor.ts index 087dfb8dc7..4ed456a86e 100644 --- a/src/panels/config/automation/manual-automation-editor.ts +++ b/src/panels/config/automation/manual-automation-editor.ts @@ -14,7 +14,7 @@ import { Trigger, triggerAutomationActions, } from "../../../data/automation"; -import { Action, MODES, MODES_MAX } from "../../../data/script"; +import { Action, isMaxMode, MODES } from "../../../data/script"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; @@ -27,13 +27,13 @@ import "./trigger/ha-automation-trigger"; export class HaManualAutomationEditor extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public isWide!: boolean; + @property({ type: Boolean }) public isWide!: boolean; - @property() public narrow!: boolean; + @property({ type: Boolean }) public narrow!: boolean; - @property() public config!: ManualAutomationConfig; + @property({ attribute: false }) public config!: ManualAutomationConfig; - @property() public stateObj?: HassEntity; + @property({ attribute: false }) public stateObj?: HassEntity; @state() private _showDescription = false; @@ -114,7 +114,7 @@ export class HaManualAutomationEditor extends LitElement { ` )} - ${this.config.mode && MODES_MAX.includes(this.config.mode) + ${this.config.mode && isMaxMode(this.config.mode) ? html`
+ private _computeLabel = ( + entry: SchemaUnion> + ): string => this.hass.localize( `ui.panel.config.lovelace.dashboards.detail.${ entry.name === "show_in_sidebar" diff --git a/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts b/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts index 283d1f2986..ae19bcb0de 100644 --- a/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts +++ b/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts @@ -5,7 +5,7 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; import { createCloseHeading } from "../../../../components/ha-dialog"; import "../../../../components/ha-form/ha-form"; -import { HaFormSchema } from "../../../../components/ha-form/types"; +import { SchemaUnion } from "../../../../components/ha-form/types"; import { LovelaceResourcesMutableParams } from "../../../../data/lovelace"; import { haStyleDialog } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; @@ -132,53 +132,68 @@ export class DialogLovelaceResourceDetail extends LitElement { `; } - private _schema = memoizeOne((data) => [ - { - name: "url", - required: true, - selector: { - text: {}, - }, - }, - { - name: "res_type", - required: true, - selector: { - select: { - options: [ - { - value: "module", - label: this.hass!.localize( - "ui.panel.config.lovelace.resources.types.module" - ), - }, - { - value: "css", - label: this.hass!.localize( - "ui.panel.config.lovelace.resources.types.css" - ), - }, - data.type === "js" && { - value: "js", - label: this.hass!.localize( - "ui.panel.config.lovelace.resources.types.js" - ), - }, - data.type === "html" && { - value: "html", - label: this.hass!.localize( - "ui.panel.config.lovelace.resources.types.html" - ), - }, - ].filter(Boolean), + private _schema = memoizeOne( + (data) => + [ + { + name: "url", + required: true, + selector: { + text: {}, + }, }, - }, - }, - ]); + { + name: "res_type", + required: true, + selector: { + select: { + options: [ + { + value: "module", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.module" + ), + }, + { + value: "css", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.css" + ), + }, + ...(data.type === "js" + ? ([ + { + value: "js", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.js" + ), + }, + ] as const) + : []), + ...(data.type === "html" + ? ([ + { + value: "html", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.html" + ), + }, + ] as const) + : []), + ], + }, + }, + }, + ] as const + ); - private _computeLabel = (entry: HaFormSchema): string => + private _computeLabel = ( + entry: SchemaUnion> + ): string => this.hass.localize( - `ui.panel.config.lovelace.resources.detail.${entry.name}` + `ui.panel.config.lovelace.resources.detail.${ + entry.name === "res_type" ? "type" : entry.name + }` ); private _valueChanged(ev: CustomEvent) { diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index 3a6f383f3a..f57d5feb2a 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -30,8 +30,7 @@ import "../../../components/ha-card"; import "../../../components/ha-fab"; import type { HaFormDataContainer, - HaFormSchema, - HaFormSelector, + SchemaUnion, } from "../../../components/ha-form/types"; import "../../../components/ha-icon-button"; import "../../../components/ha-svg-icon"; @@ -41,6 +40,7 @@ import { Action, deleteScript, getScriptEditorInitData, + isMaxMode, ManualScriptConfig, MODES, MODES_MAX, @@ -65,11 +65,11 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { @property() public scriptEntityId: string | null = null; - @property() public route!: Route; + @property({ attribute: false }) public route!: Route; - @property() public isWide?: boolean; + @property({ type: Boolean }) public isWide = false; - @property() public narrow!: boolean; + @property({ type: Boolean }) public narrow!: boolean; @state() private _config?: ScriptConfig; @@ -86,8 +86,12 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { @query("ha-yaml-editor", true) private _editor?: HaYamlEditor; private _schema = memoizeOne( - (hasID: boolean, useBluePrint?: boolean, currentMode?: string) => { - const schema: HaFormSchema[] = [ + ( + hasID: boolean, + useBluePrint?: boolean, + currentMode?: typeof MODES[number] + ) => + [ { name: "alias", selector: { @@ -102,49 +106,45 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { icon: {}, }, }, - ]; - - if (!hasID) { - schema.push({ - name: "id", - selector: { - text: {}, - }, - }); - } - - if (!useBluePrint) { - schema.push({ - name: "mode", - selector: { - select: { - options: MODES.map((mode) => ({ - label: ` - ${ - this.hass.localize( - `ui.panel.config.script.editor.modes.${mode}` - ) || mode - } - `, - value: mode, - })), - }, - }, - }); - } - - if (currentMode && MODES_MAX.includes(currentMode)) { - schema.push({ - name: "max", - required: true, - selector: { - number: { mode: "box", min: 1, max: Infinity }, - }, - }); - } - - return schema; - } + ...(!hasID + ? ([ + { + name: "id", + selector: { + text: {}, + }, + }, + ] as const) + : []), + ...(!useBluePrint + ? ([ + { + name: "mode", + selector: { + select: { + options: MODES.map((mode) => ({ + label: this.hass.localize( + `ui.panel.config.script.editor.modes.${mode}` + ), + value: mode, + })), + }, + }, + }, + ] as const) + : []), + ...(currentMode && isMaxMode(currentMode) + ? ([ + { + name: "max", + required: true, + selector: { + number: { mode: "box", min: 1, max: Infinity }, + }, + }, + ] as const) + : []), + ] as const ); protected render(): TemplateResult { @@ -161,10 +161,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { const data = { mode: MODES[0], icon: undefined, - max: - this._config.mode && MODES_MAX.includes(this._config.mode) - ? 10 - : undefined, + max: this._config.mode && isMaxMode(this._config.mode) ? 10 : undefined, ...this._config, id: this._entityId, }; @@ -506,15 +503,18 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { } private _computeLabelCallback = ( - schema: HaFormSelector, + schema: SchemaUnion>, data: HaFormDataContainer ): string => { switch (schema.name) { case "mode": return this.hass.localize("ui.panel.config.script.editor.modes.label"); case "max": + // Mode must be one of max modes per schema definition above return this.hass.localize( - `ui.panel.config.script.editor.max.${data.mode}` + `ui.panel.config.script.editor.max.${ + data.mode as typeof MODES_MAX[number] + }` ); default: return this.hass.localize( @@ -524,7 +524,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { }; private _computeHelperCallback = ( - schema: HaFormSelector + schema: SchemaUnion> ): string | undefined => { if (schema.name === "mode") { return this.hass.localize( @@ -562,7 +562,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { private _modeChanged(mode) { this._config = { ...this._config!, mode }; - if (!MODES_MAX.includes(mode)) { + if (!isMaxMode(mode)) { delete this._config.max; } this._dirty = true; diff --git a/src/panels/config/zone/dialog-zone-detail.ts b/src/panels/config/zone/dialog-zone-detail.ts index 07b9fcb4fa..9a0cc11313 100644 --- a/src/panels/config/zone/dialog-zone-detail.ts +++ b/src/panels/config/zone/dialog-zone-detail.ts @@ -6,7 +6,7 @@ import { fireEvent } from "../../../common/dom/fire_event"; import { addDistanceToCoord } from "../../../common/location/add_distance_to_coord"; import { createCloseHeading } from "../../../components/ha-dialog"; import "../../../components/ha-form/ha-form"; -import { HaFormSchema } from "../../../components/ha-form/types"; +import { SchemaUnion } from "../../../components/ha-form/types"; import { getZoneEditorInitData, ZoneMutableParams } from "../../../data/zone"; import { haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; @@ -123,51 +123,54 @@ class DialogZoneDetail extends LitElement { `; } - private _schema = memoizeOne((icon?: string): HaFormSchema[] => [ - { - name: "name", - required: true, - selector: { - text: {}, - }, - }, - { - name: "icon", - required: false, - selector: { - icon: {}, - }, - }, - { - name: "location", - required: true, - selector: { location: { radius: true, icon } }, - }, - { - name: "", - type: "grid", - schema: [ + private _schema = memoizeOne( + (icon?: string) => + [ { - name: "latitude", + name: "name", required: true, - selector: { text: {} }, + selector: { + text: {}, + }, }, { - name: "longitude", + name: "icon", + required: false, + selector: { + icon: {}, + }, + }, + { + name: "location", required: true, + selector: { location: { radius: true, icon } }, + }, + { + name: "", + type: "grid", + schema: [ + { + name: "latitude", + required: true, + selector: { text: {} }, + }, + { + name: "longitude", + required: true, - selector: { text: {} }, + selector: { text: {} }, + }, + ], }, - ], - }, - { name: "passive_note", type: "constant" }, - { name: "passive", selector: { boolean: {} } }, - { - name: "radius", - required: false, - selector: { number: { min: 0, max: 999999, mode: "box" } }, - }, - ]); + { name: "passive_note", type: "constant" }, + { name: "passive", selector: { boolean: {} } }, + { + name: "radius", + required: false, + selector: { number: { min: 0, max: 999999, mode: "box" } }, + }, + ] as const + ); private _formData = memoizeOne((data: ZoneMutableParams) => ({ ...data, @@ -197,8 +200,9 @@ class DialogZoneDetail extends LitElement { this._data = value; } - private _computeLabel = (entry: HaFormSchema): string => - this.hass.localize(`ui.panel.config.zone.detail.${entry.name}`); + private _computeLabel = ( + entry: SchemaUnion> + ): string => this.hass.localize(`ui.panel.config.zone.detail.${entry.name}`); private async _updateEntry() { this._submitting = true; diff --git a/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts b/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts index ed49622a37..f02e3a8908 100644 --- a/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts @@ -8,13 +8,13 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import { computeDomain } from "../../../../common/entity/compute_domain"; import { domainIcon } from "../../../../common/entity/domain_icon"; import type { LocalizeFunc } from "../../../../common/translations/localize"; -import type { HaFormSchema } from "../../../../components/ha-form/types"; +import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; import type { EntitiesCardEntityConfig } from "../../cards/types"; import type { LovelaceRowEditor } from "../../types"; import { entitiesConfigStruct } from "../structs/entities-struct"; -const SecondaryInfoValues: { [key: string]: { domains?: string[] } } = { +const SecondaryInfoValues = { none: {}, "entity-id": {}, "last-changed": {}, @@ -23,7 +23,7 @@ const SecondaryInfoValues: { [key: string]: { domains?: string[] } } = { position: { domains: ["cover"] }, "tilt-position": { domains: ["cover"] }, brightness: { domains: ["light"] }, -}; +} as const; @customElement("hui-generic-entity-row-editor") export class HuiGenericEntityRowEditor @@ -45,7 +45,7 @@ export class HuiGenericEntityRowEditor icon: string | undefined, entityState: HassEntity, localize: LocalizeFunc - ): HaFormSchema[] => { + ) => { const domain = computeDomain(entity); return [ @@ -73,23 +73,23 @@ export class HuiGenericEntityRowEditor name: "secondary_info", selector: { select: { - options: Object.keys(SecondaryInfoValues) - .filter( + options: ( + Object.keys(SecondaryInfoValues).filter( (info) => !("domains" in SecondaryInfoValues[info]) || ("domains" in SecondaryInfoValues[info] && SecondaryInfoValues[info].domains!.includes(domain)) - ) - .map((info) => ({ - value: info, - label: localize( - `ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}` - ), - })), + ) as Array + ).map((info) => ({ + value: info, + label: localize( + `ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}` + ), + })), }, }, }, - ]; + ] as const; } ); @@ -122,21 +122,19 @@ export class HuiGenericEntityRowEditor fireEvent(this, "config-changed", { config: ev.detail.value }); } - private _computeLabelCallback = (schema: HaFormSchema) => { - if (schema.name === "entity") { - return this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.entity" - ); + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { + switch (schema.name) { + case "secondary_info": + return this.hass!.localize( + `ui.panel.lovelace.editor.card.entity-row.${schema.name}` + ); + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); } - - return ( - this.hass!.localize( - `ui.panel.lovelace.editor.card.generic.${schema.name}` - ) || - this.hass!.localize( - `ui.panel.lovelace.editor.card.entity-row.${schema.name}` - ) - ); }; } diff --git a/src/panels/lovelace/editor/config-elements/hui-logbook-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-logbook-card-editor.ts index dcbc6f325b..603e2b4682 100644 --- a/src/panels/lovelace/editor/config-elements/hui-logbook-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-logbook-card-editor.ts @@ -12,7 +12,7 @@ import { } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/entity/ha-entities-picker"; -import type { HaFormSchema } from "../../../../components/ha-form/types"; +import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; import type { LogbookCardConfig } from "../../cards/types"; import type { LovelaceCardEditor } from "../../types"; @@ -29,7 +29,7 @@ const cardConfigStruct = assign( }) ); -const SCHEMA: HaFormSchema[] = [ +const SCHEMA = [ { name: "title", selector: { text: {} } }, { name: "", @@ -39,7 +39,7 @@ const SCHEMA: HaFormSchema[] = [ { name: "hours_to_show", selector: { number: { mode: "box", min: 1 } } }, ], }, -]; +] as const; @customElement("hui-logbook-card-editor") export class HuiLogbookCardEditor @@ -98,23 +98,19 @@ export class HuiLogbookCardEditor fireEvent(this, "config-changed", { config: ev.detail.value }); } - private _computeLabelCallback = (schema: HaFormSchema) => { - if (schema.name === "theme") { - return `${this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.theme" - )} (${this.hass!.localize( - "ui.panel.lovelace.editor.card.config.optional" - )})`; + private _computeLabelCallback = (schema: SchemaUnion) => { + switch (schema.name) { + case "theme": + return `${this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.theme" + )} (${this.hass!.localize( + "ui.panel.lovelace.editor.card.config.optional" + )})`; + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); } - - return ( - this.hass!.localize( - `ui.panel.lovelace.editor.card.generic.${schema.name}` - ) || - this.hass!.localize( - `ui.panel.lovelace.editor.card.logbook.${schema.name}` - ) - ); }; } diff --git a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts index 3d4e59d18c..4c23122554 100644 --- a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts @@ -26,7 +26,7 @@ import { entitiesConfigStruct } from "../structs/entities-struct"; import { EntitiesEditorEvent } from "../types"; import { configElementStyle } from "./config-elements-style"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; -import { HaFormSchema } from "../../../../components/ha-form/types"; +import { SchemaUnion } from "../../../../components/ha-form/types"; const cardConfigStruct = assign( baseLovelaceCardConfig, @@ -41,7 +41,7 @@ const cardConfigStruct = assign( }) ); -const SCHEMA: HaFormSchema[] = [ +const SCHEMA = [ { name: "title", selector: { text: {} } }, { name: "", @@ -53,7 +53,7 @@ const SCHEMA: HaFormSchema[] = [ { name: "hours_to_show", selector: { number: { mode: "box", min: 1 } } }, ], }, -]; +] as const; @customElement("hui-map-card-editor") export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor { @@ -149,11 +149,19 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor { fireEvent(this, "config-changed", { config: ev.detail.value }); } - private _computeLabelCallback = (schema: HaFormSchema) => - this.hass!.localize( - `ui.panel.lovelace.editor.card.generic.${schema.name}` - ) || - this.hass!.localize(`ui.panel.lovelace.editor.card.map.${schema.name}`); + private _computeLabelCallback = (schema: SchemaUnion) => { + switch (schema.name) { + case "dark_mode": + case "default_zoom": + return this.hass!.localize( + `ui.panel.lovelace.editor.card.map.${schema.name}` + ); + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); + } + }; static get styles(): CSSResultGroup { return [ diff --git a/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts index 236f4b2efd..d3b06a385e 100644 --- a/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts @@ -16,7 +16,7 @@ import { import { fireEvent } from "../../../../common/dom/fire_event"; import { computeDomain } from "../../../../common/entity/compute_domain"; import { domainIcon } from "../../../../common/entity/domain_icon"; -import type { HaFormSchema } from "../../../../components/ha-form/types"; +import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; import type { SensorCardConfig } from "../../cards/types"; import type { LovelaceCardEditor } from "../../types"; @@ -52,61 +52,58 @@ export class HuiSensorCardEditor } private _schema = memoizeOne( - ( - entity: string, - icon: string | undefined, - entityState: HassEntity - ): HaFormSchema[] => [ - { - name: "entity", - selector: { - entity: { domain: ["counter", "input_number", "number", "sensor"] }, + (entity: string, icon: string | undefined, entityState: HassEntity) => + [ + { + name: "entity", + selector: { + entity: { domain: ["counter", "input_number", "number", "sensor"] }, + }, }, - }, - { name: "name", selector: { text: {} } }, - { - type: "grid", - name: "", - schema: [ - { - name: "icon", - selector: { - icon: { - placeholder: icon || entityState?.attributes.icon, - fallbackPath: - !icon && !entityState?.attributes.icon && entityState - ? domainIcon(computeDomain(entity), entityState) - : undefined, + { name: "name", selector: { text: {} } }, + { + type: "grid", + name: "", + schema: [ + { + name: "icon", + selector: { + icon: { + placeholder: icon || entityState?.attributes.icon, + fallbackPath: + !icon && !entityState?.attributes.icon && entityState + ? domainIcon(computeDomain(entity), entityState) + : undefined, + }, }, }, - }, - { - name: "graph", - selector: { - select: { - options: [ - { - value: "none", - label: "None", - }, - { - value: "line", - label: "Line", - }, - ], + { + name: "graph", + selector: { + select: { + options: [ + { + value: "none", + label: "None", + }, + { + value: "line", + label: "Line", + }, + ], + }, }, }, - }, - { name: "unit", selector: { text: {} } }, - { name: "detail", selector: { boolean: {} } }, - { name: "theme", selector: { theme: {} } }, - { - name: "hours_to_show", - selector: { number: { min: 1, mode: "box" } }, - }, - ], - }, - ] + { name: "unit", selector: { text: {} } }, + { name: "detail", selector: { boolean: {} } }, + { name: "theme", selector: { theme: {} } }, + { + name: "hours_to_show", + selector: { number: { min: 1, mode: "box" } }, + }, + ], + }, + ] as const ); protected render(): TemplateResult { @@ -146,33 +143,29 @@ export class HuiSensorCardEditor fireEvent(this, "config-changed", { config }); } - private _computeLabelCallback = (schema: HaFormSchema) => { - if (schema.name === "entity") { - return this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.entity" - ); + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { + switch (schema.name) { + case "theme": + return `${this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.theme" + )} (${this.hass!.localize( + "ui.panel.lovelace.editor.card.config.optional" + )})`; + case "detail": + return this.hass!.localize( + "ui.panel.lovelace.editor.card.sensor.show_more_detail" + ); + case "graph": + return this.hass!.localize( + "ui.panel.lovelace.editor.card.sensor.graph_type" + ); + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); } - - if (schema.name === "theme") { - return `${this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.theme" - )} (${this.hass!.localize( - "ui.panel.lovelace.editor.card.config.optional" - )})`; - } - - if (schema.name === "detail") { - return this.hass!.localize( - "ui.panel.lovelace.editor.card.sensor.show_more_detail" - ); - } - - return ( - this.hass!.localize( - `ui.panel.lovelace.editor.card.generic.${schema.name}` - ) || - this.hass!.localize(`ui.panel.lovelace.editor.card.sensor.${schema.name}`) - ); }; static get styles(): CSSResultGroup { diff --git a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts index c695e56b54..879a6c8a41 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts @@ -16,7 +16,7 @@ import { import { fireEvent } from "../../../../common/dom/fire_event"; import type { LocalizeFunc } from "../../../../common/translations/localize"; import "../../../../components/entity/ha-statistics-picker"; -import type { HaFormSchema } from "../../../../components/ha-form/types"; +import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; import type { StatisticsGraphCardConfig } from "../../cards/types"; import { processConfigEntities } from "../../common/process-config-entities"; @@ -50,7 +50,7 @@ const cardConfigStruct = assign( }) ); -const periods = ["5minute", "hour", "day", "month"]; +const periods = ["5minute", "hour", "day", "month"] as const; @customElement("hui-statistics-graph-card-editor") export class HuiStatisticsGraphCardEditor @@ -71,54 +71,57 @@ export class HuiStatisticsGraphCardEditor : []; } - private _schema = memoizeOne((localize: LocalizeFunc) => [ - { name: "title", selector: { text: {} } }, - { - name: "", - type: "grid", - schema: [ + private _schema = memoizeOne( + (localize: LocalizeFunc) => + [ + { name: "title", selector: { text: {} } }, { - name: "period", - required: true, - selector: { - select: { - options: periods.map((period) => ({ - value: period, - label: localize( - `ui.panel.lovelace.editor.card.statistics-graph.periods.${period}` - ), - })), + name: "", + type: "grid", + schema: [ + { + name: "period", + required: true, + selector: { + select: { + options: periods.map((period) => ({ + value: period, + label: localize( + `ui.panel.lovelace.editor.card.statistics-graph.periods.${period}` + ), + })), + }, + }, + }, + { + name: "days_to_show", + required: true, + selector: { number: { min: 1, mode: "box" } }, + }, + { + name: "stat_types", + required: true, + type: "multi_select", + options: [ + ["mean", "Mean"], + ["min", "Min"], + ["max", "Max"], + ["sum", "Sum"], + ], + }, + { + name: "chart_type", + required: true, + type: "select", + options: [ + ["line", "Line"], + ["bar", "Bar"], + ], }, - }, - }, - { - name: "days_to_show", - required: true, - selector: { number: { min: 1, mode: "box" } }, - }, - { - name: "stat_types", - required: true, - type: "multi_select", - options: [ - ["mean", "Mean"], - ["min", "Min"], - ["max", "Max"], - ["sum", "Sum"], ], }, - { - name: "chart_type", - required: true, - type: "select", - options: [ - ["line", "Line"], - ["bar", "Bar"], - ], - }, - ], - }, - ]); + ] as const + ); protected render(): TemplateResult { if (!this.hass || !this._config) { @@ -169,13 +172,22 @@ export class HuiStatisticsGraphCardEditor }); } - private _computeLabelCallback = (schema: HaFormSchema) => - this.hass!.localize( - `ui.panel.lovelace.editor.card.generic.${schema.name}` - ) || - this.hass!.localize( - `ui.panel.lovelace.editor.card.statistics-graph.${schema.name}` - ); + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { + switch (schema.name) { + case "chart_type": + case "stat_types": + case "period": + return this.hass!.localize( + `ui.panel.lovelace.editor.card.statistics-graph.${schema.name}` + ); + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); + } + }; static styles: CSSResultGroup = css` ha-statistics-picker { diff --git a/src/panels/lovelace/editor/config-elements/hui-weather-forecast-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-weather-forecast-card-editor.ts index 70a310b5da..410371bcdd 100644 --- a/src/panels/lovelace/editor/config-elements/hui-weather-forecast-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-weather-forecast-card-editor.ts @@ -12,7 +12,7 @@ import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { UNAVAILABLE } from "../../../../data/entity"; import type { WeatherEntity } from "../../../../data/weather"; import type { LocalizeFunc } from "../../../../common/translations/localize"; -import type { HaFormSchema } from "../../../../components/ha-form/types"; +import type { SchemaUnion } from "../../../../components/ha-form/types"; const cardConfigStruct = assign( baseLovelaceCardConfig, @@ -66,12 +66,8 @@ export class HuiWeatherForecastCardEditor } private _schema = memoize( - ( - entity: string, - localize: LocalizeFunc, - hasForecast?: boolean - ): HaFormSchema[] => { - const schema: HaFormSchema[] = [ + (entity: string, localize: LocalizeFunc, hasForecast?: boolean) => + [ { name: "entity", required: true, @@ -89,38 +85,38 @@ export class HuiWeatherForecastCardEditor { name: "theme", selector: { theme: {} } }, ], }, - ]; - if (hasForecast) { - schema.push({ - name: "forecast", - selector: { - select: { - options: [ - { - value: "show_both", - label: localize( - "ui.panel.lovelace.editor.card.weather-forecast.show_both" - ), + ...(hasForecast + ? ([ + { + name: "forecast", + selector: { + select: { + options: [ + { + value: "show_both", + label: localize( + "ui.panel.lovelace.editor.card.weather-forecast.show_both" + ), + }, + { + value: "show_current", + label: localize( + "ui.panel.lovelace.editor.card.weather-forecast.show_only_current" + ), + }, + { + value: "show_forecast", + label: localize( + "ui.panel.lovelace.editor.card.weather-forecast.show_only_forecast" + ), + }, + ], + }, }, - { - value: "show_current", - label: localize( - "ui.panel.lovelace.editor.card.weather-forecast.show_only_current" - ), - }, - { - value: "show_forecast", - label: localize( - "ui.panel.lovelace.editor.card.weather-forecast.show_only_forecast" - ), - }, - ], - }, - }, - }); - } - return schema; - } + }, + ] as const) + : []), + ] as const ); protected render(): TemplateResult { @@ -175,31 +171,31 @@ export class HuiWeatherForecastCardEditor fireEvent(this, "config-changed", { config }); } - private _computeLabelCallback = (schema: HaFormSchema) => { - if (schema.name === "entity") { - return `${this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.entity" - )} (${this.hass!.localize( - "ui.panel.lovelace.editor.card.config.required" - )})`; + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { + switch (schema.name) { + case "entity": + return `${this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.entity" + )} (${this.hass!.localize( + "ui.panel.lovelace.editor.card.config.required" + )})`; + case "theme": + return `${this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.theme" + )} (${this.hass!.localize( + "ui.panel.lovelace.editor.card.config.optional" + )})`; + case "forecast": + return this.hass!.localize( + "ui.panel.lovelace.editor.card.weather-forecast.weather_to_show" + ); + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); } - - if (schema.name === "theme") { - return `${this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.theme" - )} (${this.hass!.localize( - "ui.panel.lovelace.editor.card.config.optional" - )})`; - } - - return ( - this.hass!.localize( - `ui.panel.lovelace.editor.card.generic.${schema.name}` - ) || - this.hass!.localize( - `ui.panel.lovelace.editor.card.weather_forecast.${schema.name}` - ) - ); }; } diff --git a/src/panels/lovelace/editor/view-editor/hui-view-editor.ts b/src/panels/lovelace/editor/view-editor/hui-view-editor.ts index 91c284ec73..8f34499782 100644 --- a/src/panels/lovelace/editor/view-editor/hui-view-editor.ts +++ b/src/panels/lovelace/editor/view-editor/hui-view-editor.ts @@ -4,7 +4,8 @@ import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; import { slugify } from "../../../../common/string/slugify"; -import type { HaFormSchema } from "../../../../components/ha-form/types"; +import type { LocalizeFunc } from "../../../../common/translations/localize"; +import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { LovelaceViewConfig } from "../../../../data/lovelace"; import type { HomeAssistant } from "../../../../types"; import { @@ -25,38 +26,43 @@ declare global { export class HuiViewEditor extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public isNew!: boolean; + @property({ type: Boolean }) public isNew!: boolean; @state() private _config!: LovelaceViewConfig; private _suggestedPath = false; - private _schema = memoizeOne((localize): HaFormSchema[] => [ - { name: "title", selector: { text: {} } }, - { - name: "icon", - selector: { - icon: {}, - }, - }, - { name: "path", selector: { text: {} } }, - { name: "theme", selector: { theme: {} } }, - { - name: "type", - selector: { - select: { - options: [ - DEFAULT_VIEW_LAYOUT, - SIDEBAR_VIEW_LAYOUT, - PANEL_VIEW_LAYOUT, - ].map((type) => ({ - value: type, - label: localize(`ui.panel.lovelace.editor.edit_view.types.${type}`), - })), + private _schema = memoizeOne( + (localize: LocalizeFunc) => + [ + { name: "title", selector: { text: {} } }, + { + name: "icon", + selector: { + icon: {}, + }, }, - }, - }, - ]); + { name: "path", selector: { text: {} } }, + { name: "theme", selector: { theme: {} } }, + { + name: "type", + selector: { + select: { + options: [ + DEFAULT_VIEW_LAYOUT, + SIDEBAR_VIEW_LAYOUT, + PANEL_VIEW_LAYOUT, + ].map((type) => ({ + value: type, + label: localize( + `ui.panel.lovelace.editor.edit_view.types.${type}` + ), + })), + }, + }, + }, + ] as const + ); set config(config: LovelaceViewConfig) { this._config = config; @@ -114,15 +120,19 @@ export class HuiViewEditor extends LitElement { fireEvent(this, "view-config-changed", { config }); } - private _computeLabelCallback = (schema: HaFormSchema) => { - if (schema.name === "path") { - return this.hass!.localize(`ui.panel.lovelace.editor.card.generic.url`); + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { + switch (schema.name) { + case "path": + return this.hass!.localize("ui.panel.lovelace.editor.card.generic.url"); + case "type": + return this.hass.localize("ui.panel.lovelace.editor.edit_view.type"); + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); } - return ( - this.hass!.localize( - `ui.panel.lovelace.editor.card.generic.${schema.name}` - ) || this.hass.localize("ui.panel.lovelace.editor.edit_view.type") - ); }; } diff --git a/src/translations/en.json b/src/translations/en.json index cf62a4996c..107ab50288 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2274,6 +2274,7 @@ "parallel": "Max number of parallel runs" }, "load_error_not_editable": "Only scripts inside scripts.yaml are editable.", + "load_error_unknown": "Error loading script ({err_no}).", "delete_confirm": "Are you sure you want to delete this script?", "delete_script": "Delete script", "save_script": "Save script", @@ -2756,6 +2757,7 @@ "name": "Name", "icon": "Icon", "icon_error_msg": "Icon should be in the format ''prefix:iconname'', for example: ''mdi:home''", + "location": "Map Location", "radius": "Radius", "latitude": "Latitude", "longitude": "Longitude", @@ -3936,7 +3938,6 @@ "geo_location_sources": "Geolocation Sources", "dark_mode": "Dark Mode?", "default_zoom": "Default Zoom", - "hours_to_show": "Hours to Show", "source": "Source", "description": "The Map card that allows you to display entities on a map." }, @@ -3992,6 +3993,7 @@ "weather-forecast": { "name": "Weather Forecast", "description": "The Weather Forecast card displays the weather. Very useful to include on interfaces that people display on the wall.", + "weather_to_show": "Weather to Show", "show_both": "Show current Weather and Forecast", "show_only_current": "Show only current Weather", "show_only_forecast": "Show only Forecast"