Convert YAMLTextArea to code mirror editor (#3980)

* Convert YAMLTextArea to code mirror editor

* Review comments

* Clean up
This commit is contained in:
Bram Kragten 2019-10-10 21:58:21 +02:00 committed by GitHub
parent ab75365636
commit 88c480759f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 78 additions and 56 deletions

View File

@ -3,9 +3,7 @@ import CodeMirror from "codemirror";
import "codemirror/mode/yaml/yaml"; import "codemirror/mode/yaml/yaml";
// @ts-ignore // @ts-ignore
import codeMirrorCSS from "codemirror/lib/codemirror.css"; import codeMirrorCSS from "codemirror/lib/codemirror.css";
import { HomeAssistant } from "../../../types"; import { fireEvent } from "../common/dom/fire_event";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeRTL } from "../../../common/util/compute_rtl";
import { customElement } from "lit-element"; import { customElement } from "lit-element";
declare global { declare global {
@ -17,12 +15,11 @@ declare global {
} }
} }
@customElement("hui-yaml-editor") @customElement("ha-yaml-editor")
export class HuiYamlEditor extends HTMLElement { export class HaYamlEditor extends HTMLElement {
public _hass?: HomeAssistant; public codemirror?: any;
private _autofocus = false;
public codemirror!: any; private _rtl = false;
private _value: string; private _value: string;
public constructor() { public constructor() {
@ -47,8 +44,11 @@ export class HuiYamlEditor extends HTMLElement {
background-color: var(--paper-dialog-background-color, var(--primary-background-color)); background-color: var(--paper-dialog-background-color, var(--primary-background-color));
transition: 0.2s ease border-right; transition: 0.2s ease border-right;
} }
:host(.error-state) .CodeMirror-gutters {
border-color: var(--error-state-color, red);
}
.CodeMirror-focused .CodeMirror-gutters { .CodeMirror-focused .CodeMirror-gutters {
border-right: 2px solid var(--paper-input-container-focus-color, var(--primary-color));; border-right: 2px solid var(--paper-input-container-focus-color, var(--primary-color));
} }
.CodeMirror-linenumber { .CodeMirror-linenumber {
color: var(--paper-dialog-color, var(--primary-text-color)); color: var(--paper-dialog-color, var(--primary-text-color));
@ -63,13 +63,6 @@ export class HuiYamlEditor extends HTMLElement {
</style>`; </style>`;
} }
set hass(hass: HomeAssistant) {
this._hass = hass;
if (this._hass) {
this.setScrollBarDirection();
}
}
set value(value: string) { set value(value: string) {
if (this.codemirror) { if (this.codemirror) {
if (value !== this.codemirror.getValue()) { if (value !== this.codemirror.getValue()) {
@ -83,6 +76,22 @@ export class HuiYamlEditor extends HTMLElement {
return this.codemirror.getValue(); return this.codemirror.getValue();
} }
set rtl(rtl: boolean) {
this._rtl = rtl;
this.setScrollBarDirection();
}
set autofocus(autofocus: boolean) {
this._autofocus = autofocus;
if (this.codemirror) {
this.codemirror.focus();
}
}
set error(error: boolean) {
this.classList.toggle("error-state", error);
}
get hasComments(): boolean { get hasComments(): boolean {
return this.shadowRoot!.querySelector("span.cm-comment") ? true : false; return this.shadowRoot!.querySelector("span.cm-comment") ? true : false;
} }
@ -96,16 +105,13 @@ export class HuiYamlEditor extends HTMLElement {
lineNumbers: true, lineNumbers: true,
mode: "yaml", mode: "yaml",
tabSize: 2, tabSize: 2,
autofocus: true, autofocus: this._autofocus,
viewportMargin: Infinity, viewportMargin: Infinity,
extraKeys: { extraKeys: {
Tab: "indentMore", Tab: "indentMore",
"Shift-Tab": "indentLess", "Shift-Tab": "indentLess",
}, },
gutters: gutters: this._rtl ? ["rtl-gutter", "CodeMirror-linenumbers"] : [],
this._hass && computeRTL(this._hass!)
? ["rtl-gutter", "CodeMirror-linenumbers"]
: [],
} }
); );
this.setScrollBarDirection(); this.setScrollBarDirection();
@ -120,18 +126,14 @@ export class HuiYamlEditor extends HTMLElement {
} }
private setScrollBarDirection(): void { private setScrollBarDirection(): void {
if (!this.codemirror) { if (this.codemirror) {
return; this.codemirror.getWrapperElement().classList.toggle("rtl", this._rtl);
} }
this.codemirror
.getWrapperElement()
.classList.toggle("rtl", computeRTL(this._hass!));
} }
} }
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"hui-yaml-editor": HuiYamlEditor; "ha-yaml-editor": HaYamlEditor;
} }
} }

View File

@ -20,6 +20,7 @@ declare global {
"ha-device-picker": any; "ha-device-picker": any;
"ha-device-condition-picker": any; "ha-device-condition-picker": any;
"ha-textarea": any; "ha-textarea": any;
"ha-yaml-editor": any;
"ha-service-picker": any; "ha-service-picker": any;
"mwc-button": any; "mwc-button": any;
"ha-device-trigger-picker": any; "ha-device-trigger-picker": any;

View File

@ -1,6 +1,8 @@
import { h, Component } from "preact"; import { h, Component } from "preact";
import yaml from "js-yaml"; import yaml from "js-yaml";
import "../../../components/ha-textarea"; import "../../../components/ha-yaml-editor";
// tslint:disable-next-line
import { HaYamlEditor } from "../../../components/ha-yaml-editor";
const isEmpty = (obj: object) => { const isEmpty = (obj: object) => {
for (const key in obj) { for (const key in obj) {
@ -12,6 +14,8 @@ const isEmpty = (obj: object) => {
}; };
export default class YAMLTextArea extends Component<any, any> { export default class YAMLTextArea extends Component<any, any> {
private _yamlEditor!: HaYamlEditor;
constructor(props) { constructor(props) {
super(props); super(props);
@ -34,7 +38,7 @@ export default class YAMLTextArea extends Component<any, any> {
} }
public onChange(ev) { public onChange(ev) {
const value = ev.target.value; const value = ev.detail.value;
let parsed; let parsed;
let isValid = true; let isValid = true;
@ -59,22 +63,30 @@ export default class YAMLTextArea extends Component<any, any> {
} }
} }
public componentDidMount() {
setTimeout(() => {
this._yamlEditor.codemirror.refresh();
}, 1);
}
public render({ label }, { value, isValid }) { public render({ label }, { value, isValid }) {
const style: any = { const style: any = {
minWidth: 300, minWidth: 300,
width: "100%", width: "100%",
}; };
if (!isValid) {
style.border = "1px solid red";
}
return ( return (
<ha-textarea <div>
label={label} <p>{label}</p>
value={value} <ha-yaml-editor
style={style} ref={this._storeYamlEditorRef}
onvalue-changed={this.onChange} style={style}
dir="ltr" value={value}
/> error={isValid === false}
onyaml-changed={this.onChange}
/>
</div>
); );
} }
private _storeYamlEditorRef = (yamlEditor) => (this._yamlEditor = yamlEditor);
} }

View File

@ -15,11 +15,12 @@ import { HomeAssistant } from "../../../../types";
import { LovelaceCardConfig } from "../../../../data/lovelace"; import { LovelaceCardConfig } from "../../../../data/lovelace";
import { LovelaceCardEditor } from "../../types"; import { LovelaceCardEditor } from "../../types";
import { getCardElementTag } from "../../common/get-card-element-tag"; import { getCardElementTag } from "../../common/get-card-element-tag";
import { computeRTL } from "../../../../common/util/compute_rtl";
import "../../components/hui-yaml-editor"; import "../../../../components/ha-yaml-editor";
// This is not a duplicate import, one is for types, one is for element. // This is not a duplicate import, one is for types, one is for element.
// tslint:disable-next-line // tslint:disable-next-line
import { HuiYamlEditor } from "../../components/hui-yaml-editor"; import { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { EntityConfig } from "../../entity-rows/types"; import { EntityConfig } from "../../entity-rows/types";
@ -43,7 +44,7 @@ export interface UIConfigChangedEvent extends Event {
@customElement("hui-card-editor") @customElement("hui-card-editor")
export class HuiCardEditor extends LitElement { export class HuiCardEditor extends LitElement {
@property() public hass?: HomeAssistant; @property() public hass!: HomeAssistant;
@property() private _yaml?: string; @property() private _yaml?: string;
@property() private _config?: LovelaceCardConfig; @property() private _config?: LovelaceCardConfig;
@ -93,8 +94,8 @@ export class HuiCardEditor extends LitElement {
return this._error !== undefined; return this._error !== undefined;
} }
private get _yamlEditor(): HuiYamlEditor { private get _yamlEditor(): HaYamlEditor {
return this.shadowRoot!.querySelector("hui-yaml-editor")!; return this.shadowRoot!.querySelector("ha-yaml-editor")!;
} }
public toggleMode() { public toggleMode() {
@ -120,11 +121,12 @@ export class HuiCardEditor extends LitElement {
` `
: html` : html`
<div class="yaml-editor"> <div class="yaml-editor">
<hui-yaml-editor <ha-yaml-editor
.hass=${this.hass} .autofocus=${true}
.rtl=${computeRTL(this.hass)}
.value=${this.yaml} .value=${this.yaml}
@yaml-changed=${this._handleYAMLChanged} @yaml-changed=${this._handleYAMLChanged}
></hui-yaml-editor> ></ha-yaml-editor>
</div> </div>
`} `}
${this._error ${this._error

View File

@ -14,11 +14,12 @@ import { Lovelace } from "./types";
import "../../components/ha-icon"; import "../../components/ha-icon";
import { haStyle } from "../../resources/styles"; import { haStyle } from "../../resources/styles";
import "./components/hui-yaml-editor"; import "../../components/ha-yaml-editor";
// This is not a duplicate import, one is for types, one is for element. // This is not a duplicate import, one is for types, one is for element.
// tslint:disable-next-line // tslint:disable-next-line
import { HuiYamlEditor } from "./components/hui-yaml-editor"; import { HaYamlEditor } from "../../components/ha-yaml-editor";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { computeRTL } from "../../common/util/compute_rtl";
const lovelaceStruct = struct.interface({ const lovelaceStruct = struct.interface({
title: "string?", title: "string?",
@ -27,7 +28,7 @@ const lovelaceStruct = struct.interface({
}); });
class LovelaceFullConfigEditor extends LitElement { class LovelaceFullConfigEditor extends LitElement {
public hass?: HomeAssistant; public hass!: HomeAssistant;
public lovelace?: Lovelace; public lovelace?: Lovelace;
public closeEditor?: () => void; public closeEditor?: () => void;
private _saving?: boolean; private _saving?: boolean;
@ -80,12 +81,14 @@ class LovelaceFullConfigEditor extends LitElement {
</app-toolbar> </app-toolbar>
</app-header> </app-header>
<div class="content"> <div class="content">
<hui-yaml-editor <ha-yaml-editor
.autofocus=${true}
.rtl=${computeRTL(this.hass)}
.hass="${this.hass}" .hass="${this.hass}"
@yaml-changed="${this._yamlChanged}" @yaml-changed="${this._yamlChanged}"
@yaml-save="${this._handleSave}" @yaml-save="${this._handleSave}"
> >
</hui-yaml-editor> </ha-yaml-editor>
</div> </div>
</app-header-layout> </app-header-layout>
`; `;
@ -205,8 +208,8 @@ class LovelaceFullConfigEditor extends LitElement {
this._changed = false; this._changed = false;
} }
private get yamlEditor(): HuiYamlEditor { private get yamlEditor(): HaYamlEditor {
return this.shadowRoot!.querySelector("hui-yaml-editor")!; return this.shadowRoot!.querySelector("ha-yaml-editor")!;
} }
} }

View File

@ -33,6 +33,8 @@ documentContainer.innerHTML = `<custom-style>
--scrollbar-thumb-color: rgb(194, 194, 194); --scrollbar-thumb-color: rgb(194, 194, 194);
--error-state-color: #db4437;
/* states and badges */ /* states and badges */
--state-icon-color: #44739e; --state-icon-color: #44739e;
--state-icon-active-color: #FDD835; --state-icon-active-color: #FDD835;