Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -20,6 +22,24 @@ import "../../components/hui-entity-editor";
import "../../../../components/ha-card";
import "../../../../components/ha-icon";

const entitiesConfigStruct = struct.union([
{
entity: "string",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a superstruct that validates entity ID and icon to be valid format?

  • entity ID: should contain a .
  • icon: should contain :

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we should! 👍

name: "string?",
icon: "string?",
},
"string",
]);

const cardConfigStruct = struct({
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So nice!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering, this is something we should probably export from the hui-entities-card so it can validate the config there too ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nevermind, the config editor only supports a subset so needs their own.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably, but the struct of the editor is a little bit simpler as it doesn't support special rows etc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yet 😉

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can have our special struct register types that validate objects.

So we can have something like

const struct = superstruct({
  'divider-row': validateDividerRow,
  'entity-row': validateEntityRow,
})

const validate = struct({
  entities: ['divider-row|entity-row']
})

Copy link
Copy Markdown
Member Author

@bramkragten bramkragten Nov 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And use a struct inside validateDividerRow? Or just use a struct instead of superstruct, will make code a little bit more ugly though...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah use a struct inside the validator, if that can work. Was just thinking out loud.

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;
Expand All @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions src/panels/lovelace/editor/hui-dialog-edit-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -81,7 +81,7 @@ export class HuiDialogEditCard extends LitElement {

declare global {
interface HTMLElementTagNameMap {
"hui-dialog-edit-config": HuiDialogEditCard;
"hui-dialog-edit-card": HuiDialogEditCard;
}
}

Expand Down
86 changes: 61 additions & 25 deletions src/panels/lovelace/editor/hui-edit-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -62,6 +64,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
_configElement: {},
_configValue: {},
_configState: {},
_errorMsg: {},
_uiEditor: {},
_saving: {},
_loading: {},
Expand All @@ -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);
}
}

Expand All @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -164,7 +176,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
>
</div>
`
: html``
: ""
}
</paper-dialog>
`;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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();
}
Expand All @@ -263,6 +283,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
}

private _closeDialog(): void {
this._cardId = undefined;
this._dialog.close();
}

Expand Down Expand Up @@ -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`;
Expand All @@ -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);
}
}
Expand Down
42 changes: 41 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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==
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down