From 7a8c9d7c124b06c1d63e03dde760a22498cc9d5f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 28 Jan 2019 16:03:21 +0100 Subject: [PATCH 01/14] Add code editor to YAML editor --- package.json | 1 + .../lovelace/components/hui-code-editor.ts | 60 +++++++++++++++++++ .../editor/card-editor/hui-edit-card.ts | 15 ++--- .../editor/card-editor/hui-yaml-editor.ts | 16 ++--- src/panels/lovelace/editor/yaml-ext-schema.ts | 22 ------- yarn.lock | 5 ++ 6 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 src/panels/lovelace/components/hui-code-editor.ts delete mode 100644 src/panels/lovelace/editor/yaml-ext-schema.ts diff --git a/package.json b/package.json index 231ad11ac4..b4b6d7e7f3 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@webcomponents/webcomponentsjs": "^2.2.0", "chart.js": "~2.7.2", "chartjs-chart-timeline": "^0.2.1", + "codemirror": "^5.43.0", "deep-clone-simple": "^1.1.1", "es6-object-assign": "^1.1.0", "eslint-import-resolver-webpack": "^0.10.1", diff --git a/src/panels/lovelace/components/hui-code-editor.ts b/src/panels/lovelace/components/hui-code-editor.ts new file mode 100644 index 0000000000..655250bce5 --- /dev/null +++ b/src/panels/lovelace/components/hui-code-editor.ts @@ -0,0 +1,60 @@ +import CodeMirror from "codemirror"; +import "codemirror/mode/yaml/yaml"; +// tslint:disable-next-line +import codeMirrorCSS from "codemirror/lib/codemirror.css"; +import { fireEvent } from "../../../common/dom/fire_event"; + +let _this; + +declare global { + interface HASSDomEvents { + "code-changed": { + value: string; + }; + } +} + +export class HuiCodeEditor extends HTMLElement { + public cm; + private _value; + + constructor() { + super(); + _this = this; + this._value = ""; + const shadowRoot = this.attachShadow({ mode: "open" }); + shadowRoot.innerHTML = ` + `; + } + + set value(value: string) { + if (this.cm) { + if (value !== this.cm.getValue()) { + this.cm.setValue(value); + } + } + this._value = value; + } + + public connectedCallback() { + if (!this.cm) { + this.cm = CodeMirror(this.shadowRoot, { + value: this._value, + lineNumbers: true, + mode: "yaml", + tabSize: 2, + }); + this.cm.on("changes", this._onChange); + } else { + this.cm.refresh(); + } + } + + private _onChange() { + fireEvent(_this, "code-changed", { value: _this.cm.getValue() }); + } +} + +window.customElements.define("hui-code-editor", HuiCodeEditor); diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index 7eac2fba87..4c974e7ecf 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -30,7 +30,6 @@ import "./hui-card-preview"; import { HuiCardPreview } from "./hui-card-preview"; import { LovelaceCardEditor, Lovelace } from "../../types"; import { YamlChangedEvent, ConfigValue, ConfigError } from "../types"; -import { extYamlSchema } from "../yaml-ext-schema"; import { EntityConfig } from "../../entity-rows/types"; import { getCardElementTag } from "../../common/get-card-element-tag"; import { addCard, replaceCard } from "../config-util"; @@ -217,9 +216,7 @@ export class HuiEditCard extends LitElement { const cardConf: LovelaceCardConfig = this._configValue!.format === "yaml" - ? yaml.safeLoad(this._configValue!.value!, { - schema: extYamlSchema, - }) + ? yaml.safeLoad(this._configValue!.value!) : this._configValue!.value!; try { @@ -244,9 +241,9 @@ export class HuiEditCard extends LitElement { private _handleYamlChanged(ev: YamlChangedEvent): void { this._configValue = { format: "yaml", value: ev.detail.yaml }; try { - const config = yaml.safeLoad(this._configValue.value, { - schema: extYamlSchema, - }) as LovelaceCardConfig; + const config = yaml.safeLoad( + this._configValue.value + ) as LovelaceCardConfig; this._updatePreview(config); this._configState = "OK"; } catch (err) { @@ -295,9 +292,7 @@ export class HuiEditCard extends LitElement { this._uiEditor = !this._uiEditor; } else if (this._configElement && this._configValue!.format === "yaml") { const yamlConfig = this._configValue!.value; - const cardConfig = yaml.safeLoad(yamlConfig, { - schema: extYamlSchema, - }) as LovelaceCardConfig; + const cardConfig = yaml.safeLoad(yamlConfig) as LovelaceCardConfig; this._uiEditor = !this._uiEditor; if (cardConfig.type !== this._cardType) { const succes = await this._loadConfigElement(cardConfig); diff --git a/src/panels/lovelace/editor/card-editor/hui-yaml-editor.ts b/src/panels/lovelace/editor/card-editor/hui-yaml-editor.ts index df2767d902..2b04168c5b 100644 --- a/src/panels/lovelace/editor/card-editor/hui-yaml-editor.ts +++ b/src/panels/lovelace/editor/card-editor/hui-yaml-editor.ts @@ -9,6 +9,8 @@ import "@polymer/paper-input/paper-textarea"; import { HomeAssistant } from "../../../../types"; import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../components/hui-code-editor"; + export class HuiYAMLEditor extends LitElement { protected hass?: HomeAssistant; private _yaml?: string; @@ -28,11 +30,11 @@ export class HuiYAMLEditor extends LitElement { protected render(): TemplateResult | void { return html` ${this.renderStyle()} - + @code-changed="${this._valueChanged}" + > + `; } @@ -46,11 +48,9 @@ export class HuiYAMLEditor extends LitElement { `; } - private _valueChanged(ev: Event): void { - const target = ev.target! as any; - this._yaml = target.value; + private _valueChanged(ev: CustomEvent): void { fireEvent(this, "yaml-changed", { - yaml: target.value, + yaml: ev.detail.value, }); } } diff --git a/src/panels/lovelace/editor/yaml-ext-schema.ts b/src/panels/lovelace/editor/yaml-ext-schema.ts deleted file mode 100644 index 357962d911..0000000000 --- a/src/panels/lovelace/editor/yaml-ext-schema.ts +++ /dev/null @@ -1,22 +0,0 @@ -import yaml from "js-yaml"; - -const secretYamlType = new yaml.Type("!secret", { - kind: "scalar", - construct(data) { - data = data || ""; - return "!secret " + data; - }, -}); - -const includeYamlType = new yaml.Type("!include", { - kind: "scalar", - construct(data) { - data = data || ""; - return "!include " + data; - }, -}); - -export const extYamlSchema = yaml.Schema.create([ - secretYamlType, - includeYamlType, -]); diff --git a/yarn.lock b/yarn.lock index e7c12feae1..0facc9d04e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4281,6 +4281,11 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +codemirror@^5.43.0: + version "5.43.0" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.43.0.tgz#2454b5e0f7005dc9945ab7b0d9594ccf233da040" + integrity sha512-mljwQWUaWIf85I7QwTBryF2ASaIvmYAL4s5UCanCJFfKeXOKhrqdHWdHiZWAMNT+hjLTCnVx2S/SYTORIgxsgA== + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" From 9f4ae5d932f0bb8e0c72426664c3e98644b1453e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 28 Jan 2019 16:35:55 +0100 Subject: [PATCH 02/14] Add code editor to raw config editor --- .../lovelace/components/hui-code-editor.ts | 10 +++ src/panels/lovelace/hui-editor.ts | 77 ++++++------------- 2 files changed, 35 insertions(+), 52 deletions(-) diff --git a/src/panels/lovelace/components/hui-code-editor.ts b/src/panels/lovelace/components/hui-code-editor.ts index 655250bce5..9a216931fb 100644 --- a/src/panels/lovelace/components/hui-code-editor.ts +++ b/src/panels/lovelace/components/hui-code-editor.ts @@ -26,10 +26,16 @@ export class HuiCodeEditor extends HTMLElement { shadowRoot.innerHTML = ` `; } set value(value: string) { + console.log(value); if (this.cm) { if (value !== this.cm.getValue()) { this.cm.setValue(value); @@ -38,6 +44,10 @@ export class HuiCodeEditor extends HTMLElement { this._value = value; } + get value() { + return this.cm.getValue(); + } + public connectedCallback() { if (!this.cm) { this.cm = CodeMirror(this.shadowRoot, { diff --git a/src/panels/lovelace/hui-editor.ts b/src/panels/lovelace/hui-editor.ts index 9e44f7cc32..893251bf93 100644 --- a/src/panels/lovelace/hui-editor.ts +++ b/src/panels/lovelace/hui-editor.ts @@ -14,8 +14,10 @@ import { Lovelace } from "./types"; import "../../components/ha-icon"; import { haStyle } from "../../resources/ha-style"; - -const TAB_INSERT = " "; +import "./components/hui-code-editor"; +// This is not a duplicate import, one is for types, one is for element. +// tslint:disable-next-line +import { HuiCodeEditor } from "./components/hui-code-editor"; const lovelaceStruct = struct.interface({ title: "string?", @@ -67,45 +69,31 @@ class LovelaceFullConfigEditor extends LitElement {
- + +
`; } protected firstUpdated() { - const textArea = this.textArea; - textArea.value = yaml.safeDump(this.lovelace!.config); - textArea.addEventListener("keydown", (e) => { + const codeEditor = this.codeEditor; + codeEditor.value = yaml.safeDump(this.lovelace!.config); + codeEditor.addEventListener("keydown", (e) => { if (e.keyCode === 51) { this._hashAdded = true; return; } - - if (e.keyCode !== 9) { + }); + codeEditor.addEventListener("code-changed", (e) => { + this._hash = this._hashAdded || this.codeEditor.value.includes("#"); + if (this._changed) { return; } - - e.preventDefault(); - - // tab was pressed, get caret position/selection - const val = textArea.value; - const start = textArea.selectionStart; - const end = textArea.selectionEnd; - - // set textarea value to: text before caret + tab + text after caret - textArea.value = - val.substring(0, start) + TAB_INSERT + val.substring(end); - - // put caret at right position again - textArea.selectionStart = textArea.selectionEnd = - start + TAB_INSERT.length; + window.onbeforeunload = () => { + return true; + }; + this._changed = true; }); } @@ -113,6 +101,10 @@ class LovelaceFullConfigEditor extends LitElement { return [ haStyle, css` + :host { + --code-mirror-height: 100%; + } + app-header-layout { height: 100vh; } @@ -132,16 +124,8 @@ class LovelaceFullConfigEditor extends LitElement { height: calc(100vh - 68px); } - textarea { - box-sizing: border-box; + hui-code-editor { height: 100%; - width: 100%; - resize: none; - border: 0; - outline: 0; - font-size: 12pt; - font-family: "Courier New", Courier, monospace; - padding: 8px; } .save-button { @@ -185,7 +169,7 @@ class LovelaceFullConfigEditor extends LitElement { let value; try { - value = yaml.safeLoad(this.textArea.value); + value = yaml.safeLoad(this.codeEditor.value); } catch (err) { alert(`Unable to parse YAML: ${err}`); this._saving = false; @@ -208,19 +192,8 @@ class LovelaceFullConfigEditor extends LitElement { this._hashAdded = false; } - private _yamlChanged() { - this._hash = this._hashAdded || this.textArea.value.includes("#"); - if (this._changed) { - return; - } - window.onbeforeunload = () => { - return true; - }; - this._changed = true; - } - - private get textArea(): HTMLTextAreaElement { - return this.shadowRoot!.querySelector("textarea")!; + private get codeEditor(): HuiCodeEditor { + return this.shadowRoot!.querySelector("hui-code-editor")! as HuiCodeEditor; } } From 644af4d009cacae025db0cc449f18a757f80a0fa Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 28 Jan 2019 17:07:02 +0100 Subject: [PATCH 03/14] Remove hui-yaml-editor --- .../lovelace/components/hui-code-editor.ts | 21 +++--- .../editor/card-editor/hui-edit-card.ts | 18 ++---- .../editor/card-editor/hui-yaml-editor.ts | 64 ------------------- 3 files changed, 17 insertions(+), 86 deletions(-) delete mode 100644 src/panels/lovelace/editor/card-editor/hui-yaml-editor.ts diff --git a/src/panels/lovelace/components/hui-code-editor.ts b/src/panels/lovelace/components/hui-code-editor.ts index 9a216931fb..ee0198965c 100644 --- a/src/panels/lovelace/components/hui-code-editor.ts +++ b/src/panels/lovelace/components/hui-code-editor.ts @@ -15,7 +15,7 @@ declare global { } export class HuiCodeEditor extends HTMLElement { - public cm; + public codemirror; private _value; constructor() { @@ -35,35 +35,34 @@ export class HuiCodeEditor extends HTMLElement { } set value(value: string) { - console.log(value); - if (this.cm) { - if (value !== this.cm.getValue()) { - this.cm.setValue(value); + if (this.codemirror) { + if (value !== this.codemirror.getValue()) { + this.codemirror.setValue(value); } } this._value = value; } get value() { - return this.cm.getValue(); + return this.codemirror.getValue(); } public connectedCallback() { - if (!this.cm) { - this.cm = CodeMirror(this.shadowRoot, { + if (!this.codemirror) { + this.codemirror = CodeMirror(this.shadowRoot, { value: this._value, lineNumbers: true, mode: "yaml", tabSize: 2, }); - this.cm.on("changes", this._onChange); + this.codemirror.on("changes", this._onChange); } else { - this.cm.refresh(); + this.codemirror.refresh(); } } private _onChange() { - fireEvent(_this, "code-changed", { value: _this.cm.getValue() }); + fireEvent(_this, "code-changed", { value: _this.codemirror.getValue() }); } } diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index 4c974e7ecf..669d2e3e3d 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -23,7 +23,7 @@ import { HomeAssistant } from "../../../../types"; import { LovelaceCardConfig } from "../../../../data/lovelace"; import { fireEvent } from "../../../../common/dom/fire_event"; -import "./hui-yaml-editor"; +import "../../components/hui-code-editor"; import "./hui-card-preview"; // This is not a duplicate import, one is for types, one is for element. // tslint:disable-next-line @@ -36,9 +36,6 @@ import { addCard, replaceCard } from "../config-util"; declare global { interface HASSDomEvents { - "yaml-changed": { - yaml: string; - }; "entities-changed": { entities: EntityConfig[]; }; @@ -118,11 +115,10 @@ export class HuiEditCard extends LitElement { ${this._uiEditor ? this._configElement : html` - + `} `; @@ -238,8 +234,8 @@ export class HuiEditCard extends LitElement { } } - private _handleYamlChanged(ev: YamlChangedEvent): void { - this._configValue = { format: "yaml", value: ev.detail.yaml }; + private _handleYamlChanged(ev: CustomEvent): void { + this._configValue = { format: "yaml", value: ev.detail.value }; try { const config = yaml.safeLoad( this._configValue.value diff --git a/src/panels/lovelace/editor/card-editor/hui-yaml-editor.ts b/src/panels/lovelace/editor/card-editor/hui-yaml-editor.ts deleted file mode 100644 index 2b04168c5b..0000000000 --- a/src/panels/lovelace/editor/card-editor/hui-yaml-editor.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { - html, - LitElement, - PropertyDeclarations, - TemplateResult, -} from "lit-element"; -import "@polymer/paper-input/paper-textarea"; - -import { HomeAssistant } from "../../../../types"; -import { fireEvent } from "../../../../common/dom/fire_event"; - -import "../../components/hui-code-editor"; - -export class HuiYAMLEditor extends LitElement { - protected hass?: HomeAssistant; - private _yaml?: string; - - static get properties(): PropertyDeclarations { - return { _yaml: {} }; - } - - set yaml(yaml: string) { - if (yaml === undefined) { - return; - } else { - this._yaml = yaml; - } - } - - protected render(): TemplateResult | void { - return html` - ${this.renderStyle()} - - - `; - } - - private renderStyle(): TemplateResult { - return html` - - `; - } - - private _valueChanged(ev: CustomEvent): void { - fireEvent(this, "yaml-changed", { - yaml: ev.detail.value, - }); - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-yaml-editor": HuiYAMLEditor; - } -} - -customElements.define("hui-yaml-editor", HuiYAMLEditor); From d2bfd5ce62f4b86df3f04f3c6e37a087b4cdbc59 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 28 Jan 2019 19:30:23 +0100 Subject: [PATCH 04/14] Update src/panels/lovelace/components/hui-code-editor.ts Co-Authored-By: bramkragten --- src/panels/lovelace/components/hui-code-editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/components/hui-code-editor.ts b/src/panels/lovelace/components/hui-code-editor.ts index ee0198965c..d52d285b30 100644 --- a/src/panels/lovelace/components/hui-code-editor.ts +++ b/src/panels/lovelace/components/hui-code-editor.ts @@ -55,7 +55,7 @@ export class HuiCodeEditor extends HTMLElement { mode: "yaml", tabSize: 2, }); - this.codemirror.on("changes", this._onChange); + this.codemirror.on("changes", () => this._onChange()); } else { this.codemirror.refresh(); } From 20ecfffb9cd6fd11bd276df77f5f48b01eb67fc8 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 28 Jan 2019 19:30:51 +0100 Subject: [PATCH 05/14] Update src/panels/lovelace/components/hui-code-editor.ts Co-Authored-By: bramkragten --- src/panels/lovelace/components/hui-code-editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/components/hui-code-editor.ts b/src/panels/lovelace/components/hui-code-editor.ts index d52d285b30..5b9149ef58 100644 --- a/src/panels/lovelace/components/hui-code-editor.ts +++ b/src/panels/lovelace/components/hui-code-editor.ts @@ -62,7 +62,7 @@ export class HuiCodeEditor extends HTMLElement { } private _onChange() { - fireEvent(_this, "code-changed", { value: _this.codemirror.getValue() }); + fireEvent(this, "code-changed", { value: this.codemirror.getValue() }); } } From d76ffd343e3f8ef1a90d018f1aa5b557b596e0cd Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 28 Jan 2019 20:41:09 +0100 Subject: [PATCH 06/14] Rename to hui-yaml-editor --- package.json | 1 + ...{hui-code-editor.ts => hui-yaml-editor.ts} | 32 +++++++++++-------- .../editor/card-editor/hui-edit-card.ts | 24 ++++++++++---- src/panels/lovelace/hui-editor.ts | 23 +++++++------ yarn.lock | 16 +++++++++- 5 files changed, 63 insertions(+), 33 deletions(-) rename src/panels/lovelace/components/{hui-code-editor.ts => hui-yaml-editor.ts} (69%) diff --git a/package.json b/package.json index b4b6d7e7f3..91464b3481 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "@babel/preset-typescript": "^7.1.0", "@gfx/zopfli": "^1.0.9", "@types/chai": "^4.1.7", + "@types/codemirror": "^0.0.71", "@types/mocha": "^5.2.5", "babel-eslint": "^10", "babel-loader": "^8.0.4", diff --git a/src/panels/lovelace/components/hui-code-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts similarity index 69% rename from src/panels/lovelace/components/hui-code-editor.ts rename to src/panels/lovelace/components/hui-yaml-editor.ts index 5b9149ef58..d1493f0dc4 100644 --- a/src/panels/lovelace/components/hui-code-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -3,24 +3,20 @@ import "codemirror/mode/yaml/yaml"; // tslint:disable-next-line import codeMirrorCSS from "codemirror/lib/codemirror.css"; import { fireEvent } from "../../../common/dom/fire_event"; - -let _this; - declare global { interface HASSDomEvents { - "code-changed": { + "yaml-changed": { value: string; }; } } -export class HuiCodeEditor extends HTMLElement { - public codemirror; - private _value; +export class HuiYamlEditor extends HTMLElement { + public codemirror: CodeMirror; + private _value: string; - constructor() { + public constructor() { super(); - _this = this; this._value = ""; const shadowRoot = this.attachShadow({ mode: "open" }); shadowRoot.innerHTML = ` @@ -43,27 +39,35 @@ export class HuiCodeEditor extends HTMLElement { this._value = value; } - get value() { + get value(): string { return this.codemirror.getValue(); } - public connectedCallback() { + public connectedCallback(): void { if (!this.codemirror) { this.codemirror = CodeMirror(this.shadowRoot, { value: this._value, lineNumbers: true, mode: "yaml", tabSize: 2, + autofocus: true, }); + fireEvent(this, "yaml-changed", { value: this._value }); this.codemirror.on("changes", () => this._onChange()); } else { this.codemirror.refresh(); } } - private _onChange() { - fireEvent(this, "code-changed", { value: this.codemirror.getValue() }); + private _onChange(): void { + fireEvent(this, "yaml-changed", { value: this.codemirror.getValue() }); } } -window.customElements.define("hui-code-editor", HuiCodeEditor); +declare global { + interface HTMLElementTagNameMap { + "hui-yaml-editor": HuiYamlEditor; + } +} + +window.customElements.define("hui-yaml-editor", HuiYamlEditor); diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index 669d2e3e3d..7693a9ac2a 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -23,13 +23,16 @@ import { HomeAssistant } from "../../../../types"; import { LovelaceCardConfig } from "../../../../data/lovelace"; import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../components/hui-code-editor"; +import "../../components/hui-yaml-editor"; +// This is not a duplicate import, one is for types, one is for element. +// tslint:disable-next-line +import { HuiYamlEditor } from "../../components/hui-yaml-editor"; import "./hui-card-preview"; // This is not a duplicate import, one is for types, one is for element. // tslint:disable-next-line import { HuiCardPreview } from "./hui-card-preview"; import { LovelaceCardEditor, Lovelace } from "../../types"; -import { YamlChangedEvent, ConfigValue, ConfigError } from "../types"; +import { ConfigValue, ConfigError } from "../types"; import { EntityConfig } from "../../entity-rows/types"; import { getCardElementTag } from "../../common/get-card-element-tag"; import { addCard, replaceCard } from "../config-util"; @@ -115,10 +118,10 @@ export class HuiEditCard extends LitElement { ${this._uiEditor ? this._configElement : html` - + @yaml-changed="${this._handleYamlChanged}" + > `} `; @@ -190,6 +193,9 @@ export class HuiEditCard extends LitElement { await this.updateComplete; this._loading = false; this._resizeDialog(); + if (!this._uiEditor) { + this.yamlEditor.codemirror.refresh(); + } } private async _resizeDialog(): Promise { @@ -256,7 +262,9 @@ export class HuiEditCard extends LitElement { this._updatePreview(value); } - private _updatePreview(config: LovelaceCardConfig) { + private async _updatePreview(config: LovelaceCardConfig) { + await this.updateComplete; + if (!this._previewEl) { return; } @@ -389,6 +397,10 @@ export class HuiEditCard extends LitElement { } } + private get yamlEditor(): HuiYamlEditor { + return this.shadowRoot!.querySelector("hui-yaml-editor")!; + } + static get styles(): CSSResult[] { return [ haStyleDialog, diff --git a/src/panels/lovelace/hui-editor.ts b/src/panels/lovelace/hui-editor.ts index 893251bf93..5260399b1f 100644 --- a/src/panels/lovelace/hui-editor.ts +++ b/src/panels/lovelace/hui-editor.ts @@ -14,10 +14,10 @@ import { Lovelace } from "./types"; import "../../components/ha-icon"; import { haStyle } from "../../resources/ha-style"; -import "./components/hui-code-editor"; +import "./components/hui-yaml-editor"; // This is not a duplicate import, one is for types, one is for element. // tslint:disable-next-line -import { HuiCodeEditor } from "./components/hui-code-editor"; +import { HuiYamlEditor } from "./components/hui-yaml-editor"; const lovelaceStruct = struct.interface({ title: "string?", @@ -69,24 +69,23 @@ class LovelaceFullConfigEditor extends LitElement {
- - +
`; } protected firstUpdated() { - const codeEditor = this.codeEditor; - codeEditor.value = yaml.safeDump(this.lovelace!.config); - codeEditor.addEventListener("keydown", (e) => { + const yamlEditor = this.yamlEditor; + yamlEditor.value = yaml.safeDump(this.lovelace!.config); + yamlEditor.addEventListener("keydown", (e) => { if (e.keyCode === 51) { this._hashAdded = true; return; } }); - codeEditor.addEventListener("code-changed", (e) => { - this._hash = this._hashAdded || this.codeEditor.value.includes("#"); + yamlEditor.addEventListener("yaml-changed", (e) => { + this._hash = this._hashAdded || this.yamlEditor.value.includes("#"); if (this._changed) { return; } @@ -169,7 +168,7 @@ class LovelaceFullConfigEditor extends LitElement { let value; try { - value = yaml.safeLoad(this.codeEditor.value); + value = yaml.safeLoad(this.yamlEditor.value); } catch (err) { alert(`Unable to parse YAML: ${err}`); this._saving = false; @@ -192,8 +191,8 @@ class LovelaceFullConfigEditor extends LitElement { this._hashAdded = false; } - private get codeEditor(): HuiCodeEditor { - return this.shadowRoot!.querySelector("hui-code-editor")! as HuiCodeEditor; + private get yamlEditor(): HuiYamlEditor { + return this.shadowRoot!.querySelector("hui-yaml-editor")!; } } diff --git a/yarn.lock b/yarn.lock index 0facc9d04e..c054a8417e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1436,6 +1436,13 @@ resolved "https://registry.yarnpkg.com/@types/clone/-/clone-0.1.30.tgz#e7365648c1b42136a59c7d5040637b3b5c83b614" integrity sha1-5zZWSMG0ITalnH1QQGN7O1yDthQ= +"@types/codemirror@^0.0.71": + version "0.0.71" + resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.71.tgz#861f1bcb3100c0a064567c5400f2981cf4ae8ca7" + integrity sha512-b2oEEnno1LIGKMR7uBEsr40al1UijF1HEpRn0+Yf1xOLl24iQgB7DBpZVMM7y54G5wCNoclDrRO65E6KHPNO2w== + dependencies: + "@types/tern" "*" + "@types/compression@^0.0.33": version "0.0.33" resolved "https://registry.yarnpkg.com/@types/compression/-/compression-0.0.33.tgz#95dc733a2339aa846381d7f1377792d2553dc27d" @@ -1477,7 +1484,7 @@ resolved "https://registry.yarnpkg.com/@types/escape-html/-/escape-html-0.0.20.tgz#cae698714dd61ebee5ab3f2aeb9a34ba1011735a" integrity sha512-6dhZJLbA7aOwkYB2GDGdIqJ20wmHnkDzaxV9PJXe7O02I2dSFTERzRB6JrX6cWKaS+VqhhY7cQUMCbO5kloFUw== -"@types/estree@0.0.39": +"@types/estree@*", "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== @@ -1850,6 +1857,13 @@ dependencies: "@types/node" "*" +"@types/tern@*": + version "0.22.1" + resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.22.1.tgz#d96467553128794f42fbe7ba8f60b520acffb817" + integrity sha512-CRzPRkg8hYLwunsj61r+rqPJQbiCIEQqlMMY/0k7krgIsoSaFgGg1ZH2f9qaR1YpenaMl6PnlTtUkCbNH/uo+A== + dependencies: + "@types/estree" "*" + "@types/through@*": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.29.tgz#72943aac922e179339c651fa34a4428a4d722f93" From 13aa0568a6685aec3d476f8f14e3617a0f4b25bd Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 29 Jan 2019 17:59:42 +0100 Subject: [PATCH 07/14] Lint and tab --- src/panels/lovelace/components/hui-yaml-editor.ts | 10 ++++++++-- src/panels/lovelace/hui-editor.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/components/hui-yaml-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts index d1493f0dc4..702ef63dc3 100644 --- a/src/panels/lovelace/components/hui-yaml-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -1,6 +1,6 @@ -import CodeMirror from "codemirror"; +import * as CodeMirror from "codemirror"; import "codemirror/mode/yaml/yaml"; -// tslint:disable-next-line +// @ts-ignore import codeMirrorCSS from "codemirror/lib/codemirror.css"; import { fireEvent } from "../../../common/dom/fire_event"; declare global { @@ -51,6 +51,12 @@ export class HuiYamlEditor extends HTMLElement { mode: "yaml", tabSize: 2, autofocus: true, + extraKeys: { + Tab: (cm: CodeMirror) => { + const spaces = Array(cm.getOption("indentUnit") + 1).join(" "); + cm.replaceSelection(spaces); + }, + }, }); fireEvent(this, "yaml-changed", { value: this._value }); this.codemirror.on("changes", () => this._onChange()); diff --git a/src/panels/lovelace/hui-editor.ts b/src/panels/lovelace/hui-editor.ts index 5260399b1f..746445bc1c 100644 --- a/src/panels/lovelace/hui-editor.ts +++ b/src/panels/lovelace/hui-editor.ts @@ -84,7 +84,7 @@ class LovelaceFullConfigEditor extends LitElement { return; } }); - yamlEditor.addEventListener("yaml-changed", (e) => { + yamlEditor.addEventListener("yaml-changed", () => { this._hash = this._hashAdded || this.yamlEditor.value.includes("#"); if (this._changed) { return; From dedb36cecf6fa32e613e92636d06b407fa8b6077 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 29 Jan 2019 18:03:34 +0100 Subject: [PATCH 08/14] Fix empty editor --- src/panels/lovelace/editor/card-editor/hui-edit-card.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index 7693a9ac2a..d8009dd746 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -194,7 +194,9 @@ export class HuiEditCard extends LitElement { this._loading = false; this._resizeDialog(); if (!this._uiEditor) { - this.yamlEditor.codemirror.refresh(); + setTimeout(() => { + this.yamlEditor.codemirror.refresh(); + }, 1); } } From 633fc1372f1e7fd436ce998ec64422358850b113 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 29 Jan 2019 18:46:40 +0100 Subject: [PATCH 09/14] Lint --- src/panels/lovelace/components/hui-yaml-editor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/components/hui-yaml-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts index 702ef63dc3..388ec016f7 100644 --- a/src/panels/lovelace/components/hui-yaml-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -1,4 +1,5 @@ -import * as CodeMirror from "codemirror"; +// @ts-ignore +import CodeMirror from "codemirror"; import "codemirror/mode/yaml/yaml"; // @ts-ignore import codeMirrorCSS from "codemirror/lib/codemirror.css"; From 43bc9abb46413b1c6ae2a9fafb95cc045f2d390a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 30 Jan 2019 12:24:13 +0100 Subject: [PATCH 10/14] Use codemirror for comment and edit detection + some styling --- .../lovelace/components/hui-yaml-editor.ts | 24 +++++++- src/panels/lovelace/hui-editor.ts | 57 +++++++++---------- 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/panels/lovelace/components/hui-yaml-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts index 388ec016f7..ba6c51cdba 100644 --- a/src/panels/lovelace/components/hui-yaml-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -9,6 +9,7 @@ declare global { "yaml-changed": { value: string; }; + "yaml-save": {}; } } @@ -18,15 +19,28 @@ export class HuiYamlEditor extends HTMLElement { public constructor() { super(); + CodeMirror.commands.save = () => { + fireEvent(this, "yaml-save"); + }; this._value = ""; const shadowRoot = this.attachShadow({ mode: "open" }); shadowRoot.innerHTML = ` `; } @@ -44,6 +58,10 @@ export class HuiYamlEditor extends HTMLElement { return this.codemirror.getValue(); } + get hasComments(): boolean { + return this.shadowRoot!.querySelector("span.cm-comment") ? true : false; + } + public connectedCallback(): void { if (!this.codemirror) { this.codemirror = CodeMirror(this.shadowRoot, { diff --git a/src/panels/lovelace/hui-editor.ts b/src/panels/lovelace/hui-editor.ts index 746445bc1c..73501d94c0 100644 --- a/src/panels/lovelace/hui-editor.ts +++ b/src/panels/lovelace/hui-editor.ts @@ -30,16 +30,13 @@ class LovelaceFullConfigEditor extends LitElement { public closeEditor?: () => void; private _saving?: boolean; private _changed?: boolean; - private _hashAdded?: boolean; - private _hash?: boolean; + private _generation?: number; static get properties() { return { lovelace: {}, _saving: {}, _changed: {}, - _hashAdded: {}, - _hash: {}, }; } @@ -53,11 +50,6 @@ class LovelaceFullConfigEditor extends LitElement { @click="${this._closeEditor}" >
Edit Config
- ${this._hash - ? html` - Comments will be not be saved! - ` - : ""} Save - + + `; } protected firstUpdated() { - const yamlEditor = this.yamlEditor; - yamlEditor.value = yaml.safeDump(this.lovelace!.config); - yamlEditor.addEventListener("keydown", (e) => { - if (e.keyCode === 51) { - this._hashAdded = true; - return; - } - }); - yamlEditor.addEventListener("yaml-changed", () => { - this._hash = this._hashAdded || this.yamlEditor.value.includes("#"); - if (this._changed) { - return; - } - window.onbeforeunload = () => { - return true; - }; - this._changed = true; - }); + this.yamlEditor.value = yaml.safeDump(this.lovelace!.config); + this.yamlEditor.codemirror.clearHistory(); + this._generation = this.yamlEditor.codemirror.changeGeneration(true); } static get styles(): CSSResult[] { @@ -141,6 +122,20 @@ class LovelaceFullConfigEditor extends LitElement { ]; } + private _yamlChanged() { + if (!this._generation) { + return; + } + this._changed = !this.yamlEditor.codemirror.isClean(this._generation); + if (this._changed && !window.onbeforeunload) { + window.onbeforeunload = () => { + return true; + }; + } else if (!this._changed && window.onbeforeunload) { + window.onbeforeunload = null; + } + } + private _closeEditor() { if (this._changed) { if ( @@ -156,10 +151,10 @@ class LovelaceFullConfigEditor extends LitElement { private async _handleSave() { this._saving = true; - if (this._hashAdded) { + if (this.yamlEditor.hasComments) { if ( !confirm( - "Your config might contain comments, these will not be saved. Do you want to continue?" + "Your config contains comment(s), these will not be saved. Do you want to continue?" ) ) { return; @@ -185,10 +180,10 @@ class LovelaceFullConfigEditor extends LitElement { } catch (err) { alert(`Unable to save YAML: ${err}`); } + this._generation = this.yamlEditor.codemirror.changeGeneration(true); window.onbeforeunload = null; this._saving = false; this._changed = false; - this._hashAdded = false; } private get yamlEditor(): HuiYamlEditor { From 95e9d6164d4f2e5f9ed1666be0259e1107be4caa Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 30 Jan 2019 12:50:57 +0100 Subject: [PATCH 11/14] Add save action (ctrl+s/cmd+s) to card editor --- src/panels/lovelace/editor/card-editor/hui-edit-card.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index d8009dd746..898df84100 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -121,6 +121,7 @@ export class HuiEditCard extends LitElement { `} From e7893801260d1d168e54509b6c5734633549cdd4 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 30 Jan 2019 20:00:13 +0100 Subject: [PATCH 12/14] Move save to the instance --- src/panels/lovelace/components/hui-yaml-editor.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/components/hui-yaml-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts index ba6c51cdba..4e531525c6 100644 --- a/src/panels/lovelace/components/hui-yaml-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -19,9 +19,6 @@ export class HuiYamlEditor extends HTMLElement { public constructor() { super(); - CodeMirror.commands.save = () => { - fireEvent(this, "yaml-save"); - }; this._value = ""; const shadowRoot = this.attachShadow({ mode: "open" }); shadowRoot.innerHTML = ` @@ -77,6 +74,9 @@ export class HuiYamlEditor extends HTMLElement { }, }, }); + this.codemirror.commands.save = () => { + fireEvent(this, "yaml-save"); + }; fireEvent(this, "yaml-changed", { value: this._value }); this.codemirror.on("changes", () => this._onChange()); } else { From db92abad66d68ff5db687a3985d453d80a72799c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 30 Jan 2019 20:59:49 +0100 Subject: [PATCH 13/14] Delete save for now --- src/panels/lovelace/components/hui-yaml-editor.ts | 4 ---- src/panels/lovelace/editor/card-editor/hui-edit-card.ts | 1 - src/panels/lovelace/hui-editor.ts | 5 +---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/panels/lovelace/components/hui-yaml-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts index 4e531525c6..a885737f31 100644 --- a/src/panels/lovelace/components/hui-yaml-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -9,7 +9,6 @@ declare global { "yaml-changed": { value: string; }; - "yaml-save": {}; } } @@ -74,9 +73,6 @@ export class HuiYamlEditor extends HTMLElement { }, }, }); - this.codemirror.commands.save = () => { - fireEvent(this, "yaml-save"); - }; fireEvent(this, "yaml-changed", { value: this._value }); this.codemirror.on("changes", () => this._onChange()); } else { diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index 898df84100..d8009dd746 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -121,7 +121,6 @@ export class HuiEditCard extends LitElement { `} diff --git a/src/panels/lovelace/hui-editor.ts b/src/panels/lovelace/hui-editor.ts index 73501d94c0..8d1c201fb9 100644 --- a/src/panels/lovelace/hui-editor.ts +++ b/src/panels/lovelace/hui-editor.ts @@ -61,10 +61,7 @@ class LovelaceFullConfigEditor extends LitElement {
- +
From c7d1417f484c64876faef8b6d6f82dda9097bea3 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 30 Jan 2019 21:09:38 +0100 Subject: [PATCH 14/14] Remove yaml-change event on init --- src/panels/lovelace/components/hui-yaml-editor.ts | 1 - src/panels/lovelace/editor/card-editor/hui-edit-card.ts | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/components/hui-yaml-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts index a885737f31..f83d392bee 100644 --- a/src/panels/lovelace/components/hui-yaml-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -73,7 +73,6 @@ export class HuiYamlEditor extends HTMLElement { }, }, }); - fireEvent(this, "yaml-changed", { value: this._value }); this.codemirror.on("changes", () => this._onChange()); } else { this.codemirror.refresh(); diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index d8009dd746..b11007465f 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -357,6 +357,7 @@ export class HuiEditCard extends LitElement { configElement = await elClass.getConfigElement(); } else { this._configValue = { format: "yaml", value: yaml.safeDump(conf) }; + this._updatePreview(conf); this._uiEditor = false; this._configElement = null; return false; @@ -373,6 +374,7 @@ export class HuiEditCard extends LitElement { format: "yaml", value: yaml.safeDump(conf), }; + this._updatePreview(conf); this._uiEditor = false; this._configElement = null; return false;