mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-14 04:46:34 +00:00
Add raw config editor for storage mode (#2246)
* Add raw config editor for storage mode * Lint * Allow inserting spaces by pressing tab
This commit is contained in:
parent
d57bcc2701
commit
0899d42967
@ -16,6 +16,8 @@ interface LovelacePanelConfig {
|
||||
mode: "yaml" | "storage";
|
||||
}
|
||||
|
||||
let editorLoaded = false;
|
||||
|
||||
class LovelacePanel extends hassLocalizeLitMixin(LitElement) {
|
||||
public panel?: PanelInfo<LovelacePanelConfig>;
|
||||
public hass?: HomeAssistant;
|
||||
@ -23,7 +25,7 @@ class LovelacePanel extends hassLocalizeLitMixin(LitElement) {
|
||||
public showMenu?: boolean;
|
||||
public route?: object;
|
||||
private _columns?: number;
|
||||
private _state?: "loading" | "loaded" | "error";
|
||||
private _state?: "loading" | "loaded" | "error" | "yaml-editor";
|
||||
private _errorMsg?: string;
|
||||
private lovelace?: Lovelace;
|
||||
private mqls?: MediaQueryList[];
|
||||
@ -42,6 +44,11 @@ class LovelacePanel extends hassLocalizeLitMixin(LitElement) {
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._closeEditor = this._closeEditor.bind(this);
|
||||
}
|
||||
|
||||
public render(): TemplateResult {
|
||||
const state = this._state!;
|
||||
|
||||
@ -80,6 +87,15 @@ class LovelacePanel extends hassLocalizeLitMixin(LitElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
if (state === "yaml-editor") {
|
||||
return html`
|
||||
<hui-editor
|
||||
.lovelace="${this.lovelace}"
|
||||
.closeEditor="${this._closeEditor}"
|
||||
></hui-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<hass-loading-screen
|
||||
.narrow="${this.narrow}"
|
||||
@ -105,6 +121,10 @@ class LovelacePanel extends hassLocalizeLitMixin(LitElement) {
|
||||
this._updateColumns();
|
||||
}
|
||||
|
||||
private _closeEditor() {
|
||||
this._state = "loaded";
|
||||
}
|
||||
|
||||
private _updateColumns() {
|
||||
const matchColumns = this.mqls!.reduce(
|
||||
(cols, mql) => cols + Number(mql.matches),
|
||||
@ -144,6 +164,13 @@ class LovelacePanel extends hassLocalizeLitMixin(LitElement) {
|
||||
config: conf,
|
||||
editMode: this.lovelace ? this.lovelace.editMode : false,
|
||||
mode: confMode,
|
||||
enableFullEditMode: () => {
|
||||
if (!editorLoaded) {
|
||||
editorLoaded = true;
|
||||
import("./hui-editor");
|
||||
}
|
||||
this._state = "yaml-editor";
|
||||
},
|
||||
setEditMode: (editMode: boolean) => {
|
||||
if (!editMode || this.lovelace!.mode !== "generated") {
|
||||
this._updateLovelace({ editMode });
|
||||
|
128
src/panels/lovelace/hui-editor.ts
Normal file
128
src/panels/lovelace/hui-editor.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { LitElement, html } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import "@polymer/paper-icon-button/paper-icon-button";
|
||||
|
||||
import { Lovelace } from "./types";
|
||||
import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
|
||||
|
||||
const TAB_INSERT = " ";
|
||||
|
||||
class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
public lovelace?: Lovelace;
|
||||
public closeEditor?: () => void;
|
||||
private _haStyle?: DocumentFragment;
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
lovelace: {},
|
||||
};
|
||||
}
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<app-header-layout>
|
||||
<app-header>
|
||||
<app-toolbar>
|
||||
<paper-icon-button
|
||||
icon="hass:close"
|
||||
@click="${this.closeEditor}"
|
||||
></paper-icon-button>
|
||||
<div main-title>Edit Config</div>
|
||||
<paper-button @click="${this._handleSave}">Save</paper-button>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<div class="content">
|
||||
<textarea
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
const textArea = this.textArea;
|
||||
textArea.value = yaml.safeDump(this.lovelace!.config);
|
||||
textArea.addEventListener("keydown", (e) => {
|
||||
if (e.keyCode !== 9) {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
protected renderStyle() {
|
||||
if (!this._haStyle) {
|
||||
this._haStyle = document.importNode(
|
||||
(document.getElementById("ha-style")!
|
||||
.children[0] as HTMLTemplateElement).content,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return html`
|
||||
${this._haStyle}
|
||||
<style>
|
||||
app-header-layout {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: calc(100% - 16px);
|
||||
width: 100%;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-size: 12pt;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleSave() {
|
||||
let value;
|
||||
try {
|
||||
value = yaml.safeLoad(this.textArea.value);
|
||||
} catch (err) {
|
||||
alert(`Unable to parse YAML: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.lovelace!.saveConfig(value);
|
||||
}
|
||||
|
||||
private get textArea(): HTMLTextAreaElement {
|
||||
return this.shadowRoot!.querySelector("textarea")!;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-editor", LovelaceFullConfigEditor);
|
@ -124,11 +124,14 @@ class HUIRoot extends NavigateMixin(
|
||||
>
|
||||
<paper-icon-button icon="hass:dots-vertical" slot="dropdown-trigger"></paper-icon-button>
|
||||
<paper-listbox on-iron-select="_deselect" slot="dropdown-content">
|
||||
<template is='dom-if' if="[[_yamlMode]]">
|
||||
<template is='dom-if' if="[[_yamlMode]]">
|
||||
<paper-item on-click="_handleRefresh">Refresh</paper-item>
|
||||
</template>
|
||||
<paper-item on-click="_handleUnusedEntities">Unused entities</paper-item>
|
||||
<paper-item on-click="_editModeEnable">[[localize("ui.panel.lovelace.editor.configure_ui")]] (alpha)</paper-item>
|
||||
<paper-item on-click="_editModeEnable">[[localize("ui.panel.lovelace.editor.configure_ui")]]</paper-item>
|
||||
<template is='dom-if' if="[[_storageMode]]">
|
||||
<paper-item on-click="_handleFullEditor">Raw config editor</paper-item>
|
||||
</template>
|
||||
<paper-item on-click="_handleHelp">Help</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
@ -196,6 +199,10 @@ class HUIRoot extends NavigateMixin(
|
||||
type: Boolean,
|
||||
computed: "_computeYamlMode(lovelace)",
|
||||
},
|
||||
_storageMode: {
|
||||
type: Boolean,
|
||||
computed: "_computeStorageMode(lovelace)",
|
||||
},
|
||||
_editMode: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
@ -287,6 +294,10 @@ class HUIRoot extends NavigateMixin(
|
||||
window.open("https://www.home-assistant.io/lovelace/", "_blank");
|
||||
}
|
||||
|
||||
_handleFullEditor() {
|
||||
this.lovelace.enableFullEditMode();
|
||||
}
|
||||
|
||||
_editModeEnable() {
|
||||
if (this._yamlMode) {
|
||||
window.alert("The edit UI is not available when in YAML mode.");
|
||||
@ -432,6 +443,10 @@ class HUIRoot extends NavigateMixin(
|
||||
return lovelace ? lovelace.mode === "yaml" : false;
|
||||
}
|
||||
|
||||
_computeStorageMode(lovelace) {
|
||||
return lovelace ? lovelace.mode === "storage" : false;
|
||||
}
|
||||
|
||||
_computeEditMode(lovelace) {
|
||||
return lovelace ? lovelace.editMode : false;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ export interface Lovelace {
|
||||
config: LovelaceConfig;
|
||||
editMode: boolean;
|
||||
mode: "generated" | "yaml" | "storage";
|
||||
enableFullEditMode: () => void;
|
||||
setEditMode: (editMode: boolean) => void;
|
||||
saveConfig: (newConfig: LovelaceConfig) => Promise<void>;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user