mirror of
https://github.com/home-assistant/frontend.git
synced 2025-04-26 06:17:20 +00:00
Migrate to codemirror 6 (#8382)
This commit is contained in:
parent
0f574a765b
commit
bde925a0e3
20
package.json
20
package.json
@ -23,6 +23,14 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@braintree/sanitize-url": "^5.0.0",
|
"@braintree/sanitize-url": "^5.0.0",
|
||||||
|
"@codemirror/commands": "^0.17.2",
|
||||||
|
"@codemirror/gutter": "^0.17.2",
|
||||||
|
"@codemirror/highlight": "^0.17.2",
|
||||||
|
"@codemirror/legacy-modes": "^0.17.1",
|
||||||
|
"@codemirror/state": "^0.17.1",
|
||||||
|
"@codemirror/stream-parser": "^0.17.1",
|
||||||
|
"@codemirror/text": "^0.17.2",
|
||||||
|
"@codemirror/view": "^0.17.7",
|
||||||
"@formatjs/intl-getcanonicallocales": "^1.4.6",
|
"@formatjs/intl-getcanonicallocales": "^1.4.6",
|
||||||
"@formatjs/intl-pluralrules": "^3.4.10",
|
"@formatjs/intl-pluralrules": "^3.4.10",
|
||||||
"@fullcalendar/common": "5.1.0",
|
"@fullcalendar/common": "5.1.0",
|
||||||
@ -177,7 +185,7 @@
|
|||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-config-airbnb-typescript": "^7.2.1",
|
"eslint-config-airbnb-typescript": "^7.2.1",
|
||||||
"eslint-config-prettier": "^6.10.1",
|
"eslint-config-prettier": "^6.10.1",
|
||||||
"eslint-import-resolver-webpack": "^0.12.2",
|
"eslint-import-resolver-webpack": "^0.13.0",
|
||||||
"eslint-plugin-disable": "^2.0.1",
|
"eslint-plugin-disable": "^2.0.1",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
"eslint-plugin-lit": "^1.2.0",
|
"eslint-plugin-lit": "^1.2.0",
|
||||||
@ -213,16 +221,16 @@
|
|||||||
"sinon": "^7.3.1",
|
"sinon": "^7.3.1",
|
||||||
"source-map-url": "^0.4.0",
|
"source-map-url": "^0.4.0",
|
||||||
"systemjs": "^6.3.2",
|
"systemjs": "^6.3.2",
|
||||||
"terser-webpack-plugin": "^5.0.0",
|
"terser-webpack-plugin": "^5.1.1",
|
||||||
"ts-lit-plugin": "^1.2.1",
|
"ts-lit-plugin": "^1.2.1",
|
||||||
"ts-mocha": "^7.0.0",
|
"ts-mocha": "^7.0.0",
|
||||||
"typescript": "^4.0.3",
|
"typescript": "^4.0.3",
|
||||||
"vinyl-buffer": "^1.0.1",
|
"vinyl-buffer": "^1.0.1",
|
||||||
"vinyl-source-stream": "^2.0.0",
|
"vinyl-source-stream": "^2.0.0",
|
||||||
"webpack": "5.1.3",
|
"webpack": "^5.24.1",
|
||||||
"webpack-cli": "4.1.0",
|
"webpack-cli": "^4.5.0",
|
||||||
"webpack-dev-server": "^3.11.0",
|
"webpack-dev-server": "^3.11.2",
|
||||||
"webpack-manifest-plugin": "~3.0.0",
|
"webpack-manifest-plugin": "^3.0.0",
|
||||||
"workbox-build": "^5.1.3"
|
"workbox-build": "^5.1.3"
|
||||||
},
|
},
|
||||||
"_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page",
|
"_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Editor } from "codemirror";
|
import type { StreamLanguage } from "@codemirror/stream-parser";
|
||||||
|
import type { EditorView, KeyBinding, ViewUpdate } from "@codemirror/view";
|
||||||
import {
|
import {
|
||||||
customElement,
|
customElement,
|
||||||
internalProperty,
|
internalProperty,
|
||||||
@ -15,32 +16,40 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const modeTag = Symbol("mode");
|
||||||
|
|
||||||
|
const readOnlyTag = Symbol("readOnly");
|
||||||
|
|
||||||
|
const saveKeyBinding: KeyBinding = {
|
||||||
|
key: "Mod-s",
|
||||||
|
run: (view: EditorView) => {
|
||||||
|
fireEvent(view.dom, "editor-save");
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("ha-code-editor")
|
@customElement("ha-code-editor")
|
||||||
export class HaCodeEditor extends UpdatingElement {
|
export class HaCodeEditor extends UpdatingElement {
|
||||||
public codemirror?: Editor;
|
public codemirror?: EditorView;
|
||||||
|
|
||||||
@property() public mode?: string;
|
@property() public mode = "yaml";
|
||||||
|
|
||||||
@property({ type: Boolean }) public autofocus = false;
|
@property({ type: Boolean }) public autofocus = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public readOnly = false;
|
@property({ type: Boolean }) public readOnly = false;
|
||||||
|
|
||||||
@property() public rtl = false;
|
|
||||||
|
|
||||||
@property() public error = false;
|
@property() public error = false;
|
||||||
|
|
||||||
@internalProperty() private _value = "";
|
@internalProperty() private _value = "";
|
||||||
|
|
||||||
|
@internalProperty() private _langs?: Record<string, StreamLanguage<unknown>>;
|
||||||
|
|
||||||
public set value(value: string) {
|
public set value(value: string) {
|
||||||
this._value = value;
|
this._value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get value(): string {
|
public get value(): string {
|
||||||
return this.codemirror ? this.codemirror.getValue() : this._value;
|
return this.codemirror ? this.codemirror.state.doc.toString() : this._value;
|
||||||
}
|
|
||||||
|
|
||||||
public get hasComments(): boolean {
|
|
||||||
return !!this.shadowRoot!.querySelector("span.cm-comment");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
@ -48,7 +57,6 @@ export class HaCodeEditor extends UpdatingElement {
|
|||||||
if (!this.codemirror) {
|
if (!this.codemirror) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.codemirror.refresh();
|
|
||||||
if (this.autofocus !== false) {
|
if (this.autofocus !== false) {
|
||||||
this.codemirror.focus();
|
this.codemirror.focus();
|
||||||
}
|
}
|
||||||
@ -62,17 +70,27 @@ export class HaCodeEditor extends UpdatingElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (changedProps.has("mode")) {
|
if (changedProps.has("mode")) {
|
||||||
this.codemirror.setOption("mode", this.mode);
|
this.codemirror.dispatch({
|
||||||
|
reconfigure: {
|
||||||
|
[modeTag]: this._mode,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (changedProps.has("autofocus")) {
|
if (changedProps.has("readOnly")) {
|
||||||
this.codemirror.setOption("autofocus", this.autofocus !== false);
|
this.codemirror.dispatch({
|
||||||
|
reconfigure: {
|
||||||
|
[readOnlyTag]: !this.readOnly,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (changedProps.has("_value") && this._value !== this.value) {
|
if (changedProps.has("_value") && this._value !== this.value) {
|
||||||
this.codemirror.setValue(this._value);
|
this.codemirror.dispatch({
|
||||||
}
|
changes: {
|
||||||
if (changedProps.has("rtl")) {
|
from: 0,
|
||||||
this.codemirror.setOption("gutters", this._calcGutters());
|
to: this.codemirror.state.doc.length,
|
||||||
this._setScrollBarDirection();
|
insert: this._value,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (changedProps.has("error")) {
|
if (changedProps.has("error")) {
|
||||||
this.classList.toggle("error-state", this.error);
|
this.classList.toggle("error-state", this.error);
|
||||||
@ -85,159 +103,62 @@ export class HaCodeEditor extends UpdatingElement {
|
|||||||
this._load();
|
this._load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get _mode() {
|
||||||
|
return this._langs![this.mode];
|
||||||
|
}
|
||||||
|
|
||||||
private async _load(): Promise<void> {
|
private async _load(): Promise<void> {
|
||||||
const loaded = await loadCodeMirror();
|
const loaded = await loadCodeMirror();
|
||||||
|
|
||||||
const codeMirror = loaded.codeMirror;
|
this._langs = loaded.langs;
|
||||||
|
|
||||||
const shadowRoot = this.attachShadow({ mode: "open" });
|
const shadowRoot = this.attachShadow({ mode: "open" });
|
||||||
|
|
||||||
shadowRoot!.innerHTML = `
|
shadowRoot!.innerHTML = `<style>
|
||||||
<style>
|
:host(.error-state) div.cm-wrap .cm-gutters {
|
||||||
${loaded.codeMirrorCss}
|
|
||||||
.CodeMirror {
|
|
||||||
height: var(--code-mirror-height, auto);
|
|
||||||
direction: var(--code-mirror-direction, ltr);
|
|
||||||
font-family: var(--code-font-family, monospace);
|
|
||||||
}
|
|
||||||
.CodeMirror-scroll {
|
|
||||||
max-height: var(--code-mirror-max-height, --code-mirror-height);
|
|
||||||
}
|
|
||||||
:host(.error-state) .CodeMirror-gutters {
|
|
||||||
border-color: var(--error-state-color, red);
|
border-color: var(--error-state-color, red);
|
||||||
}
|
}
|
||||||
.CodeMirror-focused .CodeMirror-gutters {
|
|
||||||
border-right: 2px solid var(--paper-input-container-focus-color, var(--primary-color));
|
|
||||||
}
|
|
||||||
.CodeMirror-linenumber {
|
|
||||||
color: var(--paper-dialog-color, var(--secondary-text-color));
|
|
||||||
}
|
|
||||||
.rtl .CodeMirror-vscrollbar {
|
|
||||||
right: auto;
|
|
||||||
left: 0px;
|
|
||||||
}
|
|
||||||
.rtl-gutter {
|
|
||||||
width: 20px;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutters {
|
|
||||||
border-right: 1px solid var(--paper-input-container-color, var(--secondary-text-color));
|
|
||||||
background-color: var(--paper-dialog-background-color, var(--primary-background-color));
|
|
||||||
transition: 0.2s ease border-right;
|
|
||||||
}
|
|
||||||
.cm-s-default.CodeMirror {
|
|
||||||
background-color: var(--code-editor-background-color, var(--card-background-color));
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
.cm-s-default .CodeMirror-cursor {
|
|
||||||
border-left: 1px solid var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default div.CodeMirror-selected, .cm-s-default.CodeMirror-focused div.CodeMirror-selected {
|
|
||||||
background: rgba(var(--rgb-primary-color), 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .CodeMirror-line::selection,
|
|
||||||
.cm-s-default .CodeMirror-line>span::selection,
|
|
||||||
.cm-s-default .CodeMirror-line>span>span::selection {
|
|
||||||
background: rgba(var(--rgb-primary-color), 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-keyword {
|
|
||||||
color: var(--codemirror-keyword, #6262FF);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-operator {
|
|
||||||
color: var(--codemirror-operator, #cda869);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-variable-2 {
|
|
||||||
color: var(--codemirror-variable-2, #690);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-builtin {
|
|
||||||
color: var(--codemirror-builtin, #9B7536);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-atom {
|
|
||||||
color: var(--codemirror-atom, #F90);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-number {
|
|
||||||
color: var(--codemirror-number, #ca7841);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-def {
|
|
||||||
color: var(--codemirror-def, #8DA6CE);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-string {
|
|
||||||
color: var(--codemirror-string, #07a);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-string-2 {
|
|
||||||
color: var(--codemirror-string-2, #bd6b18);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-comment {
|
|
||||||
color: var(--codemirror-comment, #777);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-variable {
|
|
||||||
color: var(--codemirror-variable, #07a);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-tag {
|
|
||||||
color: var(--codemirror-tag, #997643);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-meta {
|
|
||||||
color: var(--codemirror-meta, var(--primary-text-color));
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-attribute {
|
|
||||||
color: var(--codemirror-attribute, #d6bb6d);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-property {
|
|
||||||
color: var(--codemirror-property, #905);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-qualifier {
|
|
||||||
color: var(--codemirror-qualifier, #690);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-variable-3 {
|
|
||||||
color: var(--codemirror-variable-3, #07a);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-type {
|
|
||||||
color: var(--codemirror-type, #07a);
|
|
||||||
}
|
|
||||||
</style>`;
|
</style>`;
|
||||||
|
|
||||||
this.codemirror = codeMirror(shadowRoot, {
|
const container = document.createElement("span");
|
||||||
value: this._value,
|
|
||||||
lineNumbers: true,
|
shadowRoot.appendChild(container);
|
||||||
tabSize: 2,
|
|
||||||
mode: this.mode,
|
this.codemirror = new loaded.EditorView({
|
||||||
autofocus: this.autofocus !== false,
|
state: loaded.EditorState.create({
|
||||||
viewportMargin: Infinity,
|
doc: this._value,
|
||||||
readOnly: this.readOnly,
|
extensions: [
|
||||||
extraKeys: {
|
loaded.lineNumbers(),
|
||||||
Tab: "indentMore",
|
loaded.keymap.of([
|
||||||
"Shift-Tab": "indentLess",
|
...loaded.defaultKeymap,
|
||||||
},
|
loaded.defaultTabBinding,
|
||||||
gutters: this._calcGutters(),
|
saveKeyBinding,
|
||||||
|
]),
|
||||||
|
loaded.tagExtension(modeTag, this._mode),
|
||||||
|
loaded.theme,
|
||||||
|
loaded.Prec.fallback(loaded.highlightStyle),
|
||||||
|
loaded.EditorView.updateListener.of((update) =>
|
||||||
|
this._onUpdate(update)
|
||||||
|
),
|
||||||
|
loaded.tagExtension(
|
||||||
|
readOnlyTag,
|
||||||
|
loaded.EditorView.editable.of(!this.readOnly)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
root: shadowRoot,
|
||||||
|
parent: container,
|
||||||
});
|
});
|
||||||
this._setScrollBarDirection();
|
|
||||||
this.codemirror!.on("changes", () => this._onChange());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _blockKeyboardShortcuts() {
|
private _blockKeyboardShortcuts() {
|
||||||
this.addEventListener("keydown", (ev) => ev.stopPropagation());
|
this.addEventListener("keydown", (ev) => ev.stopPropagation());
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onChange(): void {
|
private _onUpdate(update: ViewUpdate): void {
|
||||||
|
if (!update.docChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const newValue = this.value;
|
const newValue = this.value;
|
||||||
if (newValue === this._value) {
|
if (newValue === this._value) {
|
||||||
return;
|
return;
|
||||||
@ -245,16 +166,6 @@ export class HaCodeEditor extends UpdatingElement {
|
|||||||
this._value = newValue;
|
this._value = newValue;
|
||||||
fireEvent(this, "value-changed", { value: this._value });
|
fireEvent(this, "value-changed", { value: this._value });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _calcGutters(): string[] {
|
|
||||||
return this.rtl ? ["rtl-gutter", "CodeMirror-linenumbers"] : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _setScrollBarDirection(): void {
|
|
||||||
if (this.codemirror) {
|
|
||||||
this.codemirror.getWrapperElement().classList.toggle("rtl", this.rtl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -5,20 +5,10 @@ import {
|
|||||||
internalProperty,
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
query,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { afterNextRender } from "../common/util/render-status";
|
|
||||||
import "./ha-code-editor";
|
import "./ha-code-editor";
|
||||||
import type { HaCodeEditor } from "./ha-code-editor";
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
// for fire event
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"editor-refreshed": undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isEmpty = (obj: Record<string, unknown>): boolean => {
|
const isEmpty = (obj: Record<string, unknown>): boolean => {
|
||||||
if (typeof obj !== "object") {
|
if (typeof obj !== "object") {
|
||||||
@ -44,8 +34,6 @@ export class HaYamlEditor extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _yaml = "";
|
@internalProperty() private _yaml = "";
|
||||||
|
|
||||||
@query("ha-code-editor") private _editor?: HaCodeEditor;
|
|
||||||
|
|
||||||
public setValue(value): void {
|
public setValue(value): void {
|
||||||
try {
|
try {
|
||||||
this._yaml = value && !isEmpty(value) ? safeDump(value) : "";
|
this._yaml = value && !isEmpty(value) ? safeDump(value) : "";
|
||||||
@ -54,12 +42,6 @@ export class HaYamlEditor extends LitElement {
|
|||||||
console.error(err, value);
|
console.error(err, value);
|
||||||
alert(`There was an error converting to YAML: ${err}`);
|
alert(`There was an error converting to YAML: ${err}`);
|
||||||
}
|
}
|
||||||
afterNextRender(() => {
|
|
||||||
if (this._editor?.codemirror) {
|
|
||||||
this._editor.codemirror.refresh();
|
|
||||||
}
|
|
||||||
afterNextRender(() => fireEvent(this, "editor-refreshed"));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(): void {
|
protected firstUpdated(): void {
|
||||||
|
@ -282,7 +282,7 @@ export class HuiDialogEditCard extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _opened() {
|
private _opened() {
|
||||||
this._cardEditorEl?.refreshYamlEditor();
|
this._cardEditorEl?.focusYamlEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _canSave(): boolean {
|
private get _canSave(): boolean {
|
||||||
|
@ -61,8 +61,8 @@ export class HuiConditionalCardEditor extends LitElement
|
|||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshYamlEditor(focus) {
|
public focusYamlEditor() {
|
||||||
this._cardEditorEl?.refreshYamlEditor(focus);
|
this._cardEditorEl?.focusYamlEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
@ -54,8 +54,8 @@ export class HuiStackCardEditor extends LitElement
|
|||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshYamlEditor(focus) {
|
public focusYamlEditor() {
|
||||||
this._cardEditorEl?.refreshYamlEditor(focus);
|
this._cardEditorEl?.focusYamlEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
@ -157,17 +157,14 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
this.GUImode = !this.GUImode;
|
this.GUImode = !this.GUImode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshYamlEditor(focus = false) {
|
public focusYamlEditor() {
|
||||||
if (this._configElement?.refreshYamlEditor) {
|
if (this._configElement?.focusYamlEditor) {
|
||||||
this._configElement.refreshYamlEditor(focus);
|
this._configElement.focusYamlEditor();
|
||||||
}
|
}
|
||||||
if (!this._yamlEditor?.codemirror) {
|
if (!this._yamlEditor?.codemirror) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._yamlEditor.codemirror.refresh();
|
this._yamlEditor.codemirror.focus();
|
||||||
if (focus) {
|
|
||||||
this._yamlEditor.codemirror.focus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getConfigElement(): Promise<
|
protected async getConfigElement(): Promise<
|
||||||
@ -290,7 +287,7 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
|
|
||||||
if (this._configElementType !== this.configElementType) {
|
if (this._configElementType !== this.configElementType) {
|
||||||
// If the type has changed, we need to load a new GUI editor
|
// If the type has changed, we need to load a new GUI editor
|
||||||
this._guiSupported = false;
|
this._guiSupported = undefined;
|
||||||
this._configElement = undefined;
|
this._configElement = undefined;
|
||||||
|
|
||||||
if (!this.configElementType) {
|
if (!this.configElementType) {
|
||||||
|
@ -47,8 +47,6 @@ class LovelaceFullConfigEditor extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _changed?: boolean;
|
@internalProperty() private _changed?: boolean;
|
||||||
|
|
||||||
private _generation = 1;
|
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<ha-app-layout>
|
<ha-app-layout>
|
||||||
@ -133,11 +131,7 @@ class LovelaceFullConfigEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
height: calc(100vh - 68px);
|
height: calc(100vh - var(--header-height));
|
||||||
}
|
|
||||||
|
|
||||||
hui-code-editor {
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.save-button {
|
.save-button {
|
||||||
@ -154,15 +148,11 @@ class LovelaceFullConfigEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _yamlChanged() {
|
private _yamlChanged() {
|
||||||
this._changed = !this.yamlEditor
|
this._changed = true;
|
||||||
.codemirror!.getDoc()
|
if (!window.onbeforeunload) {
|
||||||
.isClean(this._generation);
|
|
||||||
if (this._changed && !window.onbeforeunload) {
|
|
||||||
window.onbeforeunload = () => {
|
window.onbeforeunload = () => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
} else if (!this._changed && window.onbeforeunload) {
|
|
||||||
window.onbeforeunload = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +214,7 @@ class LovelaceFullConfigEditor extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.yamlEditor.hasComments) {
|
if (/^#|\s#/gm.test(value)) {
|
||||||
if (
|
if (
|
||||||
!confirm(
|
!confirm(
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
@ -281,9 +271,6 @@ class LovelaceFullConfigEditor extends LitElement {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._generation = this.yamlEditor
|
|
||||||
.codemirror!.getDoc()
|
|
||||||
.changeGeneration(true);
|
|
||||||
window.onbeforeunload = null;
|
window.onbeforeunload = null;
|
||||||
this._saving = false;
|
this._saving = false;
|
||||||
this._changed = false;
|
this._changed = false;
|
||||||
|
@ -86,5 +86,5 @@ export interface LovelaceGenericElementEditor extends HTMLElement {
|
|||||||
hass?: HomeAssistant;
|
hass?: HomeAssistant;
|
||||||
lovelace?: LovelaceConfig;
|
lovelace?: LovelaceConfig;
|
||||||
setConfig(config: any): void;
|
setConfig(config: any): void;
|
||||||
refreshYamlEditor?: (focus: boolean) => void;
|
focusYamlEditor?: () => void;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
interface LoadedCodeMirror {
|
let loaded: Promise<typeof import("./codemirror")>;
|
||||||
codeMirror: any;
|
|
||||||
codeMirrorCss: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
let loaded: Promise<LoadedCodeMirror>;
|
export const loadCodeMirror = async (): Promise<
|
||||||
|
typeof import("./codemirror")
|
||||||
export const loadCodeMirror = async (): Promise<LoadedCodeMirror> => {
|
> => {
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
loaded = import("./codemirror");
|
loaded = import("./codemirror");
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,122 @@
|
|||||||
// @ts-ignore
|
import { HighlightStyle, tags } from "@codemirror/highlight";
|
||||||
import _CodeMirror, { Editor } from "codemirror";
|
import { EditorView as CMEditorView } from "@codemirror/view";
|
||||||
// @ts-ignore
|
import { StreamLanguage } from "@codemirror/stream-parser";
|
||||||
import _codeMirrorCss from "codemirror/lib/codemirror.css";
|
import { jinja2 } from "@codemirror/legacy-modes/mode/jinja2";
|
||||||
import "codemirror/mode/jinja2/jinja2";
|
import { yaml } from "@codemirror/legacy-modes/mode/yaml";
|
||||||
import "codemirror/mode/yaml/yaml";
|
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
|
||||||
|
|
||||||
// @ts-ignore
|
export { keymap } from "@codemirror/view";
|
||||||
_CodeMirror.commands.save = (cm: Editor) => {
|
export { CMEditorView as EditorView };
|
||||||
fireEvent(cm.getWrapperElement(), "editor-save");
|
export { EditorState, Prec, tagExtension } from "@codemirror/state";
|
||||||
|
export { defaultKeymap, defaultTabBinding } from "@codemirror/commands";
|
||||||
|
export { lineNumbers } from "@codemirror/gutter";
|
||||||
|
|
||||||
|
export const langs = {
|
||||||
|
jinja2: StreamLanguage.define(jinja2),
|
||||||
|
yaml: StreamLanguage.define(yaml),
|
||||||
};
|
};
|
||||||
export const codeMirror: any = _CodeMirror;
|
|
||||||
export const codeMirrorCss: any = _codeMirrorCss;
|
export const theme = CMEditorView.theme({
|
||||||
|
$: {
|
||||||
|
color: "var(--primary-text-color)",
|
||||||
|
backgroundColor:
|
||||||
|
"var(--code-editor-background-color, var(--card-background-color))",
|
||||||
|
"& ::selection": { backgroundColor: "rgba(var(--rgb-primary-color), 0.2)" },
|
||||||
|
caretColor: "var(--secondary-text-color)",
|
||||||
|
height: "var(--code-mirror-height, auto)",
|
||||||
|
},
|
||||||
|
|
||||||
|
$$focused: { outline: "none" },
|
||||||
|
|
||||||
|
"$$focused $cursor": { borderLeftColor: "#var(--secondary-text-color)" },
|
||||||
|
"$$focused $selectionBackground, $selectionBackground": {
|
||||||
|
backgroundColor: "rgba(var(--rgb-primary-color), 0.2)",
|
||||||
|
},
|
||||||
|
|
||||||
|
$gutters: {
|
||||||
|
backgroundColor:
|
||||||
|
"var(--paper-dialog-background-color, var(--primary-background-color))",
|
||||||
|
color: "var(--paper-dialog-color, var(--secondary-text-color))",
|
||||||
|
border: "none",
|
||||||
|
borderRight:
|
||||||
|
"1px solid var(--paper-input-container-color, var(--secondary-text-color))",
|
||||||
|
},
|
||||||
|
"$$focused $gutters": {
|
||||||
|
borderRight:
|
||||||
|
"2px solid var(--paper-input-container-focus-color, var(--primary-color))",
|
||||||
|
},
|
||||||
|
"$gutterElementags.lineNumber": { color: "inherit" },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const highlightStyle = HighlightStyle.define(
|
||||||
|
{ tag: tags.keyword, color: "var(--codemirror-keyword, #6262FF)" },
|
||||||
|
{
|
||||||
|
tag: [
|
||||||
|
tags.name,
|
||||||
|
tags.deleted,
|
||||||
|
tags.character,
|
||||||
|
tags.propertyName,
|
||||||
|
tags.macroName,
|
||||||
|
],
|
||||||
|
color: "var(--codemirror-property, #905)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.function(tags.variableName), tags.labelName],
|
||||||
|
color: "var(--codemirror-variable, #07a)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)],
|
||||||
|
color: "var(--codemirror-qualifier, #690)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.definition(tags.name), tags.separator],
|
||||||
|
color: "var(--codemirror-def, #8DA6CE)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [
|
||||||
|
tags.typeName,
|
||||||
|
tags.className,
|
||||||
|
tags.number,
|
||||||
|
tags.changed,
|
||||||
|
tags.annotation,
|
||||||
|
tags.modifier,
|
||||||
|
tags.self,
|
||||||
|
tags.namespace,
|
||||||
|
],
|
||||||
|
color: "var(--codemirror-number, #ca7841)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [
|
||||||
|
tags.operator,
|
||||||
|
tags.operatorKeyword,
|
||||||
|
tags.url,
|
||||||
|
tags.escape,
|
||||||
|
tags.regexp,
|
||||||
|
tags.link,
|
||||||
|
tags.special(tags.string),
|
||||||
|
],
|
||||||
|
color: "var(--codemirror-operator, #cda869)",
|
||||||
|
},
|
||||||
|
{ tag: tags.comment, color: "var(--codemirror-comment, #777)" },
|
||||||
|
{
|
||||||
|
tag: tags.meta,
|
||||||
|
color: "var(--codemirror-meta, var(--primary-text-color))",
|
||||||
|
},
|
||||||
|
{ tag: tags.strong, fontWeight: "bold" },
|
||||||
|
{ tag: tags.emphasis, fontStyle: "italic" },
|
||||||
|
{
|
||||||
|
tag: tags.link,
|
||||||
|
color: "var(--primary-color)",
|
||||||
|
textDecoration: "underline",
|
||||||
|
},
|
||||||
|
{ tag: tags.heading, fontWeight: "bold" },
|
||||||
|
{ tag: tags.atom, color: "var(--codemirror-atom, #F90)" },
|
||||||
|
{ tag: tags.bool, color: "var(--codemirror-atom, #F90)" },
|
||||||
|
{
|
||||||
|
tag: tags.special(tags.variableName),
|
||||||
|
color: "var(--codemirror-variable-2, #690)",
|
||||||
|
},
|
||||||
|
{ tag: tags.processingInstruction, color: "var(--secondary-text-color)" },
|
||||||
|
{ tag: tags.string, color: "var(--codemirror-string, #07a)" },
|
||||||
|
{ tag: tags.inserted, color: "var(--codemirror-string2, #07a)" },
|
||||||
|
{ tag: tags.invalid, color: "var(--error-color)" }
|
||||||
|
);
|
||||||
|
@ -2652,7 +2652,7 @@
|
|||||||
"confirm_remove_config_title": "Are you sure you want to remove your Lovelace UI configuration?",
|
"confirm_remove_config_title": "Are you sure you want to remove your Lovelace UI configuration?",
|
||||||
"confirm_remove_config_text": "We will automatically generate your Lovelace UI views with your areas and devices if you remove your Lovelace UI configuration.",
|
"confirm_remove_config_text": "We will automatically generate your Lovelace UI views with your areas and devices if you remove your Lovelace UI configuration.",
|
||||||
"confirm_unsaved_changes": "You have unsaved changes, are you sure you want to exit?",
|
"confirm_unsaved_changes": "You have unsaved changes, are you sure you want to exit?",
|
||||||
"confirm_unsaved_comments": "Your configuration contains comment(s), these will not be saved. Do you want to continue?",
|
"confirm_unsaved_comments": "Your configuration might contains comment(s), these will not be saved. Do you want to continue?",
|
||||||
"error_parse_yaml": "Unable to parse YAML: {error}",
|
"error_parse_yaml": "Unable to parse YAML: {error}",
|
||||||
"error_invalid_config": "Your configuration is not valid: {error}",
|
"error_invalid_config": "Your configuration is not valid: {error}",
|
||||||
"error_save_yaml": "Unable to save YAML: {error}",
|
"error_save_yaml": "Unable to save YAML: {error}",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user