mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Check if config is compatible with UI editor (#2137)
* Check config on loading UI editor * Clean up * Add theme and reload editor on reopen * Reload config element on card type change * Clean * Clean * Add superstruct for config validation * Take content logic out off return edit-card * Reverse logic to prevent unnecessary yaml load
This commit is contained in:
parent
5e43d9b6b7
commit
90cea56a1e
@ -87,6 +87,7 @@
|
||||
"react-big-calendar": "^0.19.2",
|
||||
"regenerator-runtime": "^0.12.1",
|
||||
"round-slider": "^1.3.2",
|
||||
"superstruct": "^0.6.0",
|
||||
"unfetch": "^4.0.1",
|
||||
"web-animations-js": "^2.3.1",
|
||||
"xss": "^1.0.3"
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { struct } from "superstruct";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-toggle-button/paper-toggle-button";
|
||||
|
||||
import { processEditorEntities } from "../process-editor-entities";
|
||||
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
@ -20,6 +22,24 @@ import "../../components/hui-entity-editor";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-icon";
|
||||
|
||||
const entitiesConfigStruct = struct.union([
|
||||
{
|
||||
entity: "string",
|
||||
name: "string?",
|
||||
icon: "string?",
|
||||
},
|
||||
"string",
|
||||
]);
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
id: "string|number",
|
||||
title: "string|number?",
|
||||
theme: "string?",
|
||||
show_header_toggle: "boolean?",
|
||||
entities: [entitiesConfigStruct],
|
||||
});
|
||||
|
||||
export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
@ -39,6 +59,8 @@ export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
}
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
|
||||
this._config = { type: "entities", ...config };
|
||||
this._configEntities = processEditorEntities(config.entities);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ declare global {
|
||||
}
|
||||
|
||||
const dialogShowEvent = "show-edit-card";
|
||||
const dialogTag = "hui-dialog-edit-config";
|
||||
const dialogTag = "hui-dialog-edit-card";
|
||||
|
||||
export interface EditCardDialogParams {
|
||||
cardConfig: LovelaceCardConfig;
|
||||
@ -81,7 +81,7 @@ export class HuiDialogEditCard extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-dialog-edit-config": HuiDialogEditCard;
|
||||
"hui-dialog-edit-card": HuiDialogEditCard;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,8 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
private _loading?: boolean;
|
||||
private _isToggleAvailable?: boolean;
|
||||
private _saving: boolean;
|
||||
private _errorMsg?: TemplateResult;
|
||||
private _cardType?: string;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
@ -62,6 +64,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
_configElement: {},
|
||||
_configValue: {},
|
||||
_configState: {},
|
||||
_errorMsg: {},
|
||||
_uiEditor: {},
|
||||
_saving: {},
|
||||
_loading: {},
|
||||
@ -77,14 +80,11 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
set cardConfig(cardConfig: LovelaceCardConfig) {
|
||||
this._originalConfig = cardConfig;
|
||||
if (String(cardConfig.id) !== this._cardId) {
|
||||
this._loading = true;
|
||||
this._uiEditor = true;
|
||||
this._configElement = undefined;
|
||||
this._configValue = { format: "yaml", value: undefined };
|
||||
this._configState = "OK";
|
||||
this._isToggleAvailable = false;
|
||||
this._uiEditor = true;
|
||||
this._cardId = String(cardConfig.id);
|
||||
this._loadConfigElement();
|
||||
this._loadConfigElement(cardConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,6 +105,24 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
let content;
|
||||
if (!this._configElement !== undefined) {
|
||||
if (this._uiEditor) {
|
||||
content = html`
|
||||
<div class="element-editor">${this._configElement}</div>
|
||||
`;
|
||||
} else {
|
||||
content = html`
|
||||
<hui-yaml-editor
|
||||
.hass="${this.hass}"
|
||||
.cardId="${this._cardId}"
|
||||
.yaml="${this._configValue!.value}"
|
||||
@yaml-changed="${this._handleYamlChanged}"
|
||||
></hui-yaml-editor>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<paper-dialog with-backdrop>
|
||||
@ -118,19 +136,13 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
class="${classMap({ hidden: this._loading! })}"
|
||||
>
|
||||
${
|
||||
this._uiEditor && this._configElement !== null
|
||||
this._errorMsg
|
||||
? html`
|
||||
<div class="element-editor">${this._configElement}</div>
|
||||
`
|
||||
: html`
|
||||
<hui-yaml-editor
|
||||
.hass="${this.hass}"
|
||||
.cardId="${this._cardId}"
|
||||
.yaml="${this._configValue!.value}"
|
||||
@yaml-changed="${this._handleYamlChanged}"
|
||||
></hui-yaml-editor>
|
||||
<div class="error">${this._errorMsg}</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
${content}
|
||||
<hr />
|
||||
<hui-card-preview .hass="${this.hass}"></hui-card-preview>
|
||||
</paper-dialog-scrollable>
|
||||
@ -164,7 +176,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
>
|
||||
</div>
|
||||
`
|
||||
: html``
|
||||
: ""
|
||||
}
|
||||
</paper-dialog>
|
||||
`;
|
||||
@ -200,6 +212,10 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
.element-editor {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.error {
|
||||
color: #ef5350;
|
||||
border-bottom: 1px solid #ef5350;
|
||||
}
|
||||
hr {
|
||||
color: #000;
|
||||
opacity: 0.12;
|
||||
@ -213,7 +229,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
private _toggleEditor(): void {
|
||||
private async _toggleEditor(): Promise<void> {
|
||||
if (!this._isToggleAvailable) {
|
||||
alert("You can't switch editor.");
|
||||
return;
|
||||
@ -235,9 +251,13 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
schema: extYamlSchema,
|
||||
}),
|
||||
};
|
||||
this._configElement.setConfig(this._configValue!
|
||||
.value as LovelaceCardConfig);
|
||||
this._uiEditor = !this._uiEditor;
|
||||
const cardConfig = this._configValue!.value! as LovelaceCardConfig;
|
||||
if (cardConfig.type !== this._cardType) {
|
||||
await this._loadConfigElement(cardConfig);
|
||||
this._cardType = cardConfig.type;
|
||||
}
|
||||
this._configElement.setConfig(cardConfig);
|
||||
}
|
||||
this._resizeDialog();
|
||||
}
|
||||
@ -263,6 +283,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _closeDialog(): void {
|
||||
this._cardId = undefined;
|
||||
this._dialog.close();
|
||||
}
|
||||
|
||||
@ -363,11 +384,16 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
return JSON.stringify(configValue) !== JSON.stringify(this._originalConfig);
|
||||
}
|
||||
|
||||
private async _loadConfigElement(): Promise<void> {
|
||||
if (!this._originalConfig) {
|
||||
private async _loadConfigElement(conf: LovelaceCardConfig): Promise<void> {
|
||||
if (!conf) {
|
||||
return;
|
||||
}
|
||||
const conf = this._originalConfig;
|
||||
|
||||
this._errorMsg = undefined;
|
||||
this._loading = true;
|
||||
this._configElement = undefined;
|
||||
this._isToggleAvailable = false;
|
||||
|
||||
const tag = conf.type.startsWith(CUSTOM_TYPE_PREFIX)
|
||||
? conf!.type.substr(CUSTOM_TYPE_PREFIX.length)
|
||||
: `hui-${conf!.type}-card`;
|
||||
@ -378,20 +404,30 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
try {
|
||||
configElement = await elClass.getConfigElement();
|
||||
} catch (err) {
|
||||
this._configElement = null;
|
||||
this._uiEditor = false;
|
||||
this._configElement = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this._isToggleAvailable = true;
|
||||
try {
|
||||
configElement.setConfig(conf);
|
||||
} catch (err) {
|
||||
this._errorMsg = html`
|
||||
Your config is not supported by the UI editor:<br /><b>${err.message}</b
|
||||
><br />Falling back to YAML editor.
|
||||
`;
|
||||
this._uiEditor = false;
|
||||
this._configElement = null;
|
||||
return;
|
||||
}
|
||||
|
||||
configElement.setConfig(conf);
|
||||
configElement.hass = this.hass;
|
||||
configElement.addEventListener("config-changed", (ev) =>
|
||||
this._handleUIConfigChanged(ev.detail.config)
|
||||
);
|
||||
this._configValue = { format: "json", value: conf };
|
||||
this._configElement = configElement;
|
||||
this._isToggleAvailable = true;
|
||||
this._updatePreview(conf);
|
||||
}
|
||||
}
|
||||
|
42
yarn.lock
42
yarn.lock
@ -4226,6 +4226,16 @@ clone-buffer@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
|
||||
integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg=
|
||||
|
||||
clone-deep@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713"
|
||||
integrity sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==
|
||||
dependencies:
|
||||
for-own "^1.0.0"
|
||||
is-plain-object "^2.0.4"
|
||||
kind-of "^6.0.0"
|
||||
shallow-clone "^1.0.0"
|
||||
|
||||
clone-stats@^0.0.1, clone-stats@~0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1"
|
||||
@ -6452,6 +6462,11 @@ follow-redirects@^1.0.0:
|
||||
dependencies:
|
||||
debug "=3.1.0"
|
||||
|
||||
for-in@^0.1.3:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
|
||||
integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=
|
||||
|
||||
for-in@^1.0.1, for-in@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||
@ -8556,7 +8571,7 @@ kind-of@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
|
||||
integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
|
||||
|
||||
kind-of@^6.0.0, kind-of@^6.0.2:
|
||||
kind-of@^6.0.0, kind-of@^6.0.1, kind-of@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
|
||||
integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
|
||||
@ -9716,6 +9731,14 @@ mixin-deep@^1.2.0:
|
||||
for-in "^1.0.2"
|
||||
is-extendable "^1.0.1"
|
||||
|
||||
mixin-object@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e"
|
||||
integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=
|
||||
dependencies:
|
||||
for-in "^0.1.3"
|
||||
is-extendable "^0.1.1"
|
||||
|
||||
mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
@ -12267,6 +12290,15 @@ shady-css-parser@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/shady-css-parser/-/shady-css-parser-0.1.0.tgz#534dc79c8ca5884c5ed92a4e5a13d6d863bca428"
|
||||
integrity sha512-irfJUUkEuDlNHKZNAp2r7zOyMlmbfVJ+kWSfjlCYYUx/7dJnANLCyTzQZsuxy5NJkvtNwSxY5Gj8MOlqXUQPyA==
|
||||
|
||||
shallow-clone@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571"
|
||||
integrity sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==
|
||||
dependencies:
|
||||
is-extendable "^0.1.1"
|
||||
kind-of "^5.0.0"
|
||||
mixin-object "^2.0.1"
|
||||
|
||||
shebang-command@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||
@ -12903,6 +12935,14 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
||||
|
||||
superstruct@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.6.0.tgz#20d2073526cf683a57f258695e009c4a19134ad0"
|
||||
integrity sha512-6Y+bh5oFXCMUmGGzcdwd8M2qXMWn9aH3Qu2wV8Cg/Lxu+3fTxJ0dTx54nKd/Sm3lSz3i901xVatzev7c/xN8Lg==
|
||||
dependencies:
|
||||
clone-deep "^2.0.1"
|
||||
kind-of "^6.0.1"
|
||||
|
||||
supports-color@3.1.2, supports-color@5.4.0, supports-color@^0.2.0, supports-color@^2.0.0, supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
|
||||
|
Loading…
x
Reference in New Issue
Block a user