mirror of
https://github.com/home-assistant/frontend.git
synced 2025-04-30 08:17:20 +00:00
Added migrate dialog when card has no ID (#2008)
* Added migrate dialog when card has no ID * typos * Fix error messages * cardId should be a string * Add translation * Only load yaml in yaml editor * revert name change * Combine migrate and edit in one dialog * lint * fixes + inlude and secret yaml * resize after toggle preview -> value>config * add loading spinners * only create preview when type changes * loader on yaml editor * Fixed loading spinner not disappearing * moved dialog * disable toggle if not avail * address comments * cleanup showDialog
This commit is contained in:
parent
9a9986cf17
commit
0bb85bc895
@ -1,4 +1,5 @@
|
|||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { LovelaceConfig } from "../types";
|
||||||
|
|
||||||
export const getCardConfig = (
|
export const getCardConfig = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@ -12,10 +13,17 @@ export const getCardConfig = (
|
|||||||
export const updateCardConfig = (
|
export const updateCardConfig = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
cardId: string,
|
cardId: string,
|
||||||
config: any
|
config: LovelaceConfig | string,
|
||||||
|
configFormat: "json" | "yaml"
|
||||||
): Promise<void> =>
|
): Promise<void> =>
|
||||||
hass!.callWS({
|
hass.callWS({
|
||||||
type: "lovelace/config/card/update",
|
type: "lovelace/config/card/update",
|
||||||
card_id: cardId,
|
card_id: cardId,
|
||||||
card_config: config,
|
card_config: config,
|
||||||
|
format: configFormat,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const migrateConfig = (hass: HomeAssistant): Promise<void> =>
|
||||||
|
hass.callWS({
|
||||||
|
type: "lovelace/config/migrate",
|
||||||
});
|
});
|
||||||
|
@ -2,17 +2,16 @@ import "@polymer/paper-button/paper-button";
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { LovelaceConfig } from "../types";
|
||||||
|
|
||||||
let registeredDialog = false;
|
let registeredDialog = false;
|
||||||
|
|
||||||
export class HuiCardOptions extends LitElement {
|
export class HuiCardOptions extends LitElement {
|
||||||
public cardId?: string;
|
public cardConfig?: LovelaceConfig;
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return { hass: {} };
|
||||||
hass: {},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
@ -51,7 +50,7 @@ export class HuiCardOptions extends LitElement {
|
|||||||
private _editCard() {
|
private _editCard() {
|
||||||
fireEvent(this, "show-edit-card", {
|
fireEvent(this, "show-edit-card", {
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
cardId: this.cardId,
|
cardConfig: this.cardConfig,
|
||||||
reloadLovelace: () => fireEvent(this, "config-refresh"),
|
reloadLovelace: () => fireEvent(this, "config-refresh"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
73
src/panels/lovelace/editor/hui-card-preview.ts
Normal file
73
src/panels/lovelace/editor/hui-card-preview.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import "@polymer/paper-input/paper-textarea";
|
||||||
|
|
||||||
|
import createCardElement from "../common/create-card-element";
|
||||||
|
import createErrorCardConfig from "../common/create-error-card-config";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
|
import { ConfigError } from "./types";
|
||||||
|
|
||||||
|
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||||
|
|
||||||
|
export class HuiCardPreview extends HTMLElement {
|
||||||
|
private _hass?: HomeAssistant;
|
||||||
|
private _element?: LovelaceCard;
|
||||||
|
|
||||||
|
set hass(value: HomeAssistant) {
|
||||||
|
this._hass = value;
|
||||||
|
if (this._element) {
|
||||||
|
this._element.hass = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set error(error: ConfigError) {
|
||||||
|
const configValue = createErrorCardConfig(
|
||||||
|
`${error.type}: ${error.message}`,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
this._createCard(configValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
set config(configValue: LovelaceConfig) {
|
||||||
|
if (!configValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._element) {
|
||||||
|
this._createCard(configValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag = configValue.type.startsWith(CUSTOM_TYPE_PREFIX)
|
||||||
|
? configValue.type.substr(CUSTOM_TYPE_PREFIX.length)
|
||||||
|
: `hui-${configValue.type}-card`;
|
||||||
|
|
||||||
|
if (tag.toUpperCase() === this._element.tagName) {
|
||||||
|
this._element.setConfig(configValue);
|
||||||
|
} else {
|
||||||
|
this._createCard(configValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createCard(configValue: LovelaceConfig): void {
|
||||||
|
if (this._element) {
|
||||||
|
this.removeChild(this._element);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._element = createCardElement(configValue);
|
||||||
|
|
||||||
|
if (this._hass) {
|
||||||
|
this._element!.hass = this._hass;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.appendChild(this._element!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-card-preview": HuiCardPreview;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("hui-card-preview", HuiCardPreview);
|
@ -1,229 +1,52 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import yaml from "js-yaml";
|
|
||||||
import { when } from "lit-html/directives/when";
|
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import "@polymer/paper-button/paper-button";
|
|
||||||
import "@polymer/paper-input/paper-textarea";
|
|
||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
|
||||||
import "@polymer/paper-dialog/paper-dialog";
|
|
||||||
// This is not a duplicate import, one is for types, one is for element.
|
|
||||||
// tslint:disable-next-line
|
|
||||||
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { getCardConfig, updateCardConfig } from "../common/data";
|
import { LovelaceConfig } from "../types";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import "./hui-edit-card";
|
||||||
|
import "./hui-migrate-config";
|
||||||
import "./hui-yaml-editor";
|
|
||||||
import "./hui-yaml-card-preview";
|
|
||||||
// This is not a duplicate import, one is for types, one is for element.
|
|
||||||
// tslint:disable-next-line
|
|
||||||
import { HuiYAMLCardPreview } from "./hui-yaml-card-preview";
|
|
||||||
import { LovelaceCardEditor, LovelaceConfig } from "../types";
|
|
||||||
import { YamlChangedEvent, ConfigValue } from "./types";
|
|
||||||
|
|
||||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
|
||||||
|
|
||||||
export class HuiDialogEditCard extends LitElement {
|
export class HuiDialogEditCard extends LitElement {
|
||||||
protected hass?: HomeAssistant;
|
protected _hass?: HomeAssistant;
|
||||||
private _cardId?: string;
|
private _cardConfig?: LovelaceConfig;
|
||||||
private _originalConfigYaml?: string;
|
|
||||||
private _configElement?: LovelaceCardEditor | null;
|
|
||||||
private _reloadLovelace?: () => void;
|
private _reloadLovelace?: () => void;
|
||||||
private _editorToggle?: boolean;
|
|
||||||
private _configValue?: ConfigValue;
|
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
_hass: {},
|
||||||
cardId: {
|
_cardConfig: {},
|
||||||
type: Number,
|
|
||||||
},
|
|
||||||
_dialogClosedCallback: {},
|
|
||||||
_configElement: {},
|
|
||||||
_editorToggle: {},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog({ hass, cardId, reloadLovelace }) {
|
public async showDialog({ hass, cardConfig, reloadLovelace }): Promise<void> {
|
||||||
this.hass = hass;
|
this._hass = hass;
|
||||||
this._cardId = cardId;
|
this._cardConfig = cardConfig;
|
||||||
this._reloadLovelace = reloadLovelace;
|
this._reloadLovelace = reloadLovelace;
|
||||||
this._editorToggle = true;
|
|
||||||
this._configElement = undefined;
|
|
||||||
this._configValue = { format: "yaml", value: "" };
|
|
||||||
this._loadConfig().then(() => this._loadConfigElement());
|
|
||||||
// Wait till dialog is rendered.
|
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
this._dialog.open();
|
(this.shadowRoot!.children[0] as any).showDialog();
|
||||||
}
|
|
||||||
|
|
||||||
private get _dialog(): PaperDialogElement {
|
|
||||||
return this.shadowRoot!.querySelector("paper-dialog")!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _previewEl(): HuiYAMLCardPreview {
|
|
||||||
return this.shadowRoot!.querySelector("hui-yaml-card-preview")!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
${
|
||||||
paper-dialog {
|
this._cardConfig!.id
|
||||||
width: 650px;
|
? html`
|
||||||
}
|
<hui-edit-card
|
||||||
.element-editor {
|
.cardConfig="${this._cardConfig}"
|
||||||
margin-bottom: 16px;
|
.hass="${this._hass}"
|
||||||
}
|
@reload-lovelace="${this._reloadLovelace}"
|
||||||
</style>
|
>
|
||||||
<paper-dialog with-backdrop>
|
</hui-edit-card>
|
||||||
<h2>Card Configuration</h2>
|
`
|
||||||
<paper-dialog-scrollable>
|
: html`
|
||||||
${
|
<hui-migrate-config
|
||||||
this._editorToggle && this._configElement !== null
|
.hass="${this._hass}"
|
||||||
? html`
|
@reload-lovelace="${this._reloadLovelace}"
|
||||||
<div class="element-editor">
|
></hui-migrate-config>
|
||||||
${
|
`
|
||||||
when(
|
}
|
||||||
this._configElement,
|
|
||||||
() => this._configElement,
|
|
||||||
() =>
|
|
||||||
html`
|
|
||||||
Loading...
|
|
||||||
`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<hui-yaml-editor
|
|
||||||
.yaml="${this._configValue!.value}"
|
|
||||||
@yaml-changed="${this._handleYamlChanged}"
|
|
||||||
></hui-yaml-editor>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
<hui-yaml-card-preview
|
|
||||||
.hass="${this.hass}"
|
|
||||||
.value="${this._configValue}"
|
|
||||||
></hui-yaml-card-preview>
|
|
||||||
</paper-dialog-scrollable>
|
|
||||||
<div class="paper-dialog-buttons">
|
|
||||||
<paper-button @click="${this._toggleEditor}"
|
|
||||||
>Toggle Editor</paper-button
|
|
||||||
>
|
|
||||||
<paper-button @click="${this._closeDialog}">Cancel</paper-button>
|
|
||||||
<paper-button @click="${this._updateConfigInBackend}"
|
|
||||||
>Save</paper-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</paper-dialog>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleYamlChanged(ev: YamlChangedEvent): void {
|
|
||||||
this._configValue = { format: "yaml", value: ev.detail.yaml };
|
|
||||||
this._updatePreview(this._configValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleJSConfigChanged(value: LovelaceConfig): void {
|
|
||||||
this._configElement!.setConfig(value);
|
|
||||||
this._configValue = { format: "js", value };
|
|
||||||
this._updatePreview(this._configValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updatePreview(value: ConfigValue) {
|
|
||||||
if (!this._previewEl) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._previewEl.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _closeDialog(): void {
|
|
||||||
this._dialog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _toggleEditor(): void {
|
|
||||||
if (this._editorToggle && this._configValue!.format === "js") {
|
|
||||||
this._configValue = {
|
|
||||||
format: "yaml",
|
|
||||||
value: yaml.safeDump(this._configValue!.value),
|
|
||||||
};
|
|
||||||
} else if (this._configElement && this._configValue!.format === "yaml") {
|
|
||||||
this._configValue = {
|
|
||||||
format: "js",
|
|
||||||
value: yaml.safeLoad(this._configValue!.value),
|
|
||||||
};
|
|
||||||
this._configElement.setConfig(this._configValue!.value as LovelaceConfig);
|
|
||||||
}
|
|
||||||
this._editorToggle = !this._editorToggle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _loadConfig(): Promise<void> {
|
|
||||||
const cardConfig = await getCardConfig(this.hass!, this._cardId!);
|
|
||||||
this._configValue = {
|
|
||||||
format: "yaml",
|
|
||||||
value: cardConfig,
|
|
||||||
};
|
|
||||||
this._originalConfigYaml = cardConfig;
|
|
||||||
|
|
||||||
// This will center the dialog with the updated config Element
|
|
||||||
fireEvent(this._dialog, "iron-resize");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _loadConfigElement(): Promise<void> {
|
|
||||||
const conf = yaml.safeLoad(this._configValue!.value);
|
|
||||||
|
|
||||||
const tag = conf.type.startsWith(CUSTOM_TYPE_PREFIX)
|
|
||||||
? conf.type.substr(CUSTOM_TYPE_PREFIX.length)
|
|
||||||
: `hui-${conf.type}-card`;
|
|
||||||
|
|
||||||
const elClass = customElements.get(tag);
|
|
||||||
let configElement;
|
|
||||||
|
|
||||||
try {
|
|
||||||
configElement = await elClass.getConfigElement();
|
|
||||||
} catch (err) {
|
|
||||||
this._configElement = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
configElement.setConfig(conf);
|
|
||||||
configElement.hass = this.hass;
|
|
||||||
configElement.addEventListener("config-changed", (ev) =>
|
|
||||||
this._handleJSConfigChanged(ev.detail.config)
|
|
||||||
);
|
|
||||||
this._configValue = { format: "js", value: conf };
|
|
||||||
this._configElement = configElement;
|
|
||||||
|
|
||||||
// This will center the dialog with the updated config Element
|
|
||||||
fireEvent(this._dialog, "iron-resize");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _updateConfigInBackend(): Promise<void> {
|
|
||||||
if (this._configValue!.format === "js") {
|
|
||||||
this._configValue = {
|
|
||||||
format: "yaml",
|
|
||||||
value: yaml.safeDump(this._configValue!.value),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._configValue!.value === this._originalConfigYaml) {
|
|
||||||
this._dialog.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await updateCardConfig(
|
|
||||||
this.hass!,
|
|
||||||
this._cardId!,
|
|
||||||
this._configValue!.value
|
|
||||||
);
|
|
||||||
this._dialog.close();
|
|
||||||
this._reloadLovelace!();
|
|
||||||
} catch (err) {
|
|
||||||
alert(`Saving failed: ${err.reason}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
376
src/panels/lovelace/editor/hui-edit-card.ts
Normal file
376
src/panels/lovelace/editor/hui-edit-card.ts
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
|
import { classMap } from "lit-html/directives/classMap";
|
||||||
|
import { TemplateResult } from "lit-html";
|
||||||
|
import yaml from "js-yaml";
|
||||||
|
|
||||||
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
|
import "@polymer/paper-dialog/paper-dialog";
|
||||||
|
// This is not a duplicate import, one is for types, one is for element.
|
||||||
|
// tslint:disable-next-line
|
||||||
|
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
||||||
|
import "@polymer/paper-button/paper-button";
|
||||||
|
import "@polymer/paper-input/paper-textarea";
|
||||||
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { updateCardConfig } from "../common/data";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
|
|
||||||
|
import "./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, LovelaceConfig } from "../types";
|
||||||
|
import { YamlChangedEvent, ConfigValue, ConfigError } from "./types";
|
||||||
|
import { extYamlSchema } from "./yaml-ext-schema";
|
||||||
|
|
||||||
|
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||||
|
|
||||||
|
export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||||
|
protected hass?: HomeAssistant;
|
||||||
|
private _cardId?: string;
|
||||||
|
private _originalConfig?: LovelaceConfig;
|
||||||
|
private _configElement?: LovelaceCardEditor | null;
|
||||||
|
private _uiEditor?: boolean;
|
||||||
|
private _configValue?: ConfigValue;
|
||||||
|
private _configState?: string;
|
||||||
|
private _loading?: boolean;
|
||||||
|
private _isToggleAvailable?: boolean;
|
||||||
|
private _saving: boolean;
|
||||||
|
|
||||||
|
static get properties(): PropertyDeclarations {
|
||||||
|
return {
|
||||||
|
_hass: {},
|
||||||
|
_cardId: {},
|
||||||
|
_originalConfig: {},
|
||||||
|
_configElement: {},
|
||||||
|
_configValue: {},
|
||||||
|
_configState: {},
|
||||||
|
_uiEditor: {},
|
||||||
|
_saving: {},
|
||||||
|
_loading: {},
|
||||||
|
_isToggleAvailable: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected constructor() {
|
||||||
|
super();
|
||||||
|
this._saving = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set cardConfig(cardConfig: LovelaceConfig) {
|
||||||
|
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._cardId = String(cardConfig.id);
|
||||||
|
this._loadConfigElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async showDialog(): Promise<void> {
|
||||||
|
// Wait till dialog is rendered.
|
||||||
|
if (this._dialog == null) {
|
||||||
|
await this.updateComplete;
|
||||||
|
}
|
||||||
|
this._dialog.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _dialog(): PaperDialogElement {
|
||||||
|
return this.shadowRoot!.querySelector("paper-dialog")!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _previewEl(): HuiCardPreview {
|
||||||
|
return this.shadowRoot!.querySelector("hui-card-preview")!;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
${this.renderStyle()}
|
||||||
|
<paper-dialog with-backdrop>
|
||||||
|
<h2>${this.localize("ui.panel.lovelace.editor.edit.header")}</h2>
|
||||||
|
<paper-spinner
|
||||||
|
?active="${this._loading}"
|
||||||
|
alt="Loading"
|
||||||
|
class="center margin-bot"
|
||||||
|
></paper-spinner>
|
||||||
|
<paper-dialog-scrollable
|
||||||
|
class="${classMap({ hidden: this._loading! })}"
|
||||||
|
>
|
||||||
|
${
|
||||||
|
this._uiEditor && this._configElement !== null
|
||||||
|
? 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>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
<hui-card-preview .hass="${this.hass}"></hui-card-preview>
|
||||||
|
</paper-dialog-scrollable>
|
||||||
|
${
|
||||||
|
!this._loading
|
||||||
|
? html`
|
||||||
|
<div class="paper-dialog-buttons">
|
||||||
|
<paper-button
|
||||||
|
?disabled="${!this._isToggleAvailable}"
|
||||||
|
@click="${this._toggleEditor}"
|
||||||
|
>${
|
||||||
|
this.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit.toggle_editor"
|
||||||
|
)
|
||||||
|
}</paper-button
|
||||||
|
>
|
||||||
|
<paper-button @click="${this._closeDialog}"
|
||||||
|
>${this.localize("ui.common.cancel")}</paper-button
|
||||||
|
>
|
||||||
|
<paper-button
|
||||||
|
?disabled="${this._saving}"
|
||||||
|
@click="${this._save}"
|
||||||
|
>
|
||||||
|
<paper-spinner
|
||||||
|
?active="${this._saving}"
|
||||||
|
alt="Saving"
|
||||||
|
></paper-spinner>
|
||||||
|
${
|
||||||
|
this.localize("ui.panel.lovelace.editor.edit.save")
|
||||||
|
}</paper-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html``
|
||||||
|
}
|
||||||
|
</paper-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderStyle(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
paper-dialog {
|
||||||
|
width: 650px;
|
||||||
|
}
|
||||||
|
.center {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
.margin-bot {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
paper-button paper-spinner {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
paper-spinner {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
paper-spinner[active] {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.element-editor {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleEditor(): void {
|
||||||
|
if (!this._isToggleAvailable) {
|
||||||
|
alert("You can't switch editor.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._uiEditor && this._configValue!.format === "json") {
|
||||||
|
if (this._isConfigChanged()) {
|
||||||
|
this._configValue = {
|
||||||
|
format: "yaml",
|
||||||
|
value: yaml.safeDump(this._configValue!.value),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this._configValue = { format: "yaml", value: undefined };
|
||||||
|
}
|
||||||
|
this._uiEditor = !this._uiEditor;
|
||||||
|
} else if (this._configElement && this._configValue!.format === "yaml") {
|
||||||
|
this._configValue = {
|
||||||
|
format: "json",
|
||||||
|
value: yaml.safeLoad(this._configValue!.value, {
|
||||||
|
schema: extYamlSchema,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
this._configElement.setConfig(this._configValue!.value as LovelaceConfig);
|
||||||
|
this._uiEditor = !this._uiEditor;
|
||||||
|
}
|
||||||
|
this._resizeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _save(): void {
|
||||||
|
this._saving = true;
|
||||||
|
this._updateConfigInBackend();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _saveDone(): void {
|
||||||
|
this._saving = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadedDialog(): Promise<void> {
|
||||||
|
await this.updateComplete;
|
||||||
|
this._loading = false;
|
||||||
|
this._resizeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _resizeDialog(): Promise<void> {
|
||||||
|
await this.updateComplete;
|
||||||
|
fireEvent(this._dialog, "iron-resize");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _closeDialog(): void {
|
||||||
|
this._dialog.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateConfigInBackend(): Promise<void> {
|
||||||
|
if (!this._isConfigValid()) {
|
||||||
|
alert("Your config is not valid, please fix your config before saving.");
|
||||||
|
this._saveDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._isConfigChanged()) {
|
||||||
|
this._closeDialog();
|
||||||
|
this._saveDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateCardConfig(
|
||||||
|
this.hass!,
|
||||||
|
this._cardId!,
|
||||||
|
this._configValue!.value!,
|
||||||
|
this._configValue!.format
|
||||||
|
);
|
||||||
|
this._closeDialog();
|
||||||
|
this._saveDone();
|
||||||
|
fireEvent(this, "reload-lovelace");
|
||||||
|
} catch (err) {
|
||||||
|
alert(`Saving failed: ${err.message}`);
|
||||||
|
this._saveDone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleYamlChanged(ev: YamlChangedEvent): void {
|
||||||
|
this._configValue = { format: "yaml", value: ev.detail.yaml };
|
||||||
|
try {
|
||||||
|
const config = yaml.safeLoad(this._configValue.value, {
|
||||||
|
schema: extYamlSchema,
|
||||||
|
}) as LovelaceConfig;
|
||||||
|
this._updatePreview(config);
|
||||||
|
this._configState = "OK";
|
||||||
|
if (!this._isToggleAvailable && this._configElement !== null) {
|
||||||
|
this._isToggleAvailable = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this._isToggleAvailable = false;
|
||||||
|
this._configState = "YAML_ERROR";
|
||||||
|
this._setPreviewError({
|
||||||
|
type: "YAML Error",
|
||||||
|
message: err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleUIConfigChanged(value: LovelaceConfig): void {
|
||||||
|
this._configElement!.setConfig(value);
|
||||||
|
this._configValue = { format: "json", value };
|
||||||
|
this._updatePreview(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _updatePreview(config: LovelaceConfig) {
|
||||||
|
if (!this._previewEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._previewEl.config = config;
|
||||||
|
|
||||||
|
if (this._loading) {
|
||||||
|
this._loadedDialog();
|
||||||
|
} else {
|
||||||
|
this._resizeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setPreviewError(error: ConfigError) {
|
||||||
|
if (!this._previewEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._previewEl.error = error;
|
||||||
|
|
||||||
|
this._resizeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _isConfigValid() {
|
||||||
|
if (!this._cardId || !this._configValue || !this._configValue.value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this._configState === "OK") {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _isConfigChanged(): boolean {
|
||||||
|
const configValue =
|
||||||
|
this._configValue!.format === "yaml"
|
||||||
|
? yaml.safeDump(this._configValue!.value)
|
||||||
|
: this._configValue!.value;
|
||||||
|
return JSON.stringify(configValue) !== JSON.stringify(this._originalConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadConfigElement(): Promise<void> {
|
||||||
|
const conf = this._originalConfig;
|
||||||
|
const tag = conf!.type.startsWith(CUSTOM_TYPE_PREFIX)
|
||||||
|
? conf!.type.substr(CUSTOM_TYPE_PREFIX.length)
|
||||||
|
: `hui-${conf!.type}-card`;
|
||||||
|
|
||||||
|
const elClass = customElements.get(tag);
|
||||||
|
let configElement;
|
||||||
|
|
||||||
|
try {
|
||||||
|
configElement = await elClass.getConfigElement();
|
||||||
|
} catch (err) {
|
||||||
|
this._configElement = null;
|
||||||
|
this._uiEditor = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isToggleAvailable = true;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-edit-card": HuiEditCard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("hui-edit-card", HuiEditCard);
|
112
src/panels/lovelace/editor/hui-migrate-config.ts
Normal file
112
src/panels/lovelace/editor/hui-migrate-config.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
|
import "@polymer/paper-dialog/paper-dialog";
|
||||||
|
// This is not a duplicate import, one is for types, one is for element.
|
||||||
|
// tslint:disable-next-line
|
||||||
|
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
||||||
|
import "@polymer/paper-button/paper-button";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
|
import { migrateConfig } from "../common/data";
|
||||||
|
|
||||||
|
export class HuiMigrateConfig extends hassLocalizeLitMixin(LitElement) {
|
||||||
|
protected hass?: HomeAssistant;
|
||||||
|
private _migrating?: boolean;
|
||||||
|
|
||||||
|
static get properties(): PropertyDeclarations {
|
||||||
|
return { _hass: {}, _migrating: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _dialog(): PaperDialogElement {
|
||||||
|
return this.shadowRoot!.querySelector("paper-dialog")!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async showDialog(): Promise<void> {
|
||||||
|
// Wait till dialog is rendered.
|
||||||
|
if (this._dialog == null) {
|
||||||
|
await this.updateComplete;
|
||||||
|
}
|
||||||
|
this._dialog.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
${this.renderStyle()}
|
||||||
|
<paper-dialog with-backdrop>
|
||||||
|
<h2>${this.localize("ui.panel.lovelace.editor.migrate.header")}</h2>
|
||||||
|
<paper-dialog-scrollable>
|
||||||
|
<p>${this.localize("ui.panel.lovelace.editor.migrate.para_no_id")}</p>
|
||||||
|
<p>
|
||||||
|
${this.localize("ui.panel.lovelace.editor.migrate.para_migrate")}
|
||||||
|
</p>
|
||||||
|
</paper-dialog-scrollable>
|
||||||
|
<div class="paper-dialog-buttons">
|
||||||
|
<paper-button @click="${this._closeDialog}"
|
||||||
|
>${this.localize("ui.common.cancel")}</paper-button
|
||||||
|
>
|
||||||
|
<paper-button
|
||||||
|
?disabled="${this._migrating}"
|
||||||
|
@click="${this._migrateConfig}"
|
||||||
|
>
|
||||||
|
<paper-spinner
|
||||||
|
?active="${this._migrating}"
|
||||||
|
alt="Saving"
|
||||||
|
></paper-spinner>
|
||||||
|
${
|
||||||
|
this.localize("ui.panel.lovelace.editor.migrate.migrate")
|
||||||
|
}</paper-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderStyle(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
paper-dialog {
|
||||||
|
width: 650px;
|
||||||
|
}
|
||||||
|
paper-spinner {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
paper-spinner[active] {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
paper-button paper-spinner {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _closeDialog(): void {
|
||||||
|
this._dialog.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _migrateConfig(): Promise<void> {
|
||||||
|
this._migrating = true;
|
||||||
|
try {
|
||||||
|
await migrateConfig(this.hass!);
|
||||||
|
this._closeDialog();
|
||||||
|
fireEvent(this, "reload-lovelace");
|
||||||
|
} catch (err) {
|
||||||
|
alert(`Migration failed: ${err.message}`);
|
||||||
|
this._migrating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-migrate-config": HuiMigrateConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("hui-migrate-config", HuiMigrateConfig);
|
@ -1,57 +0,0 @@
|
|||||||
import yaml from "js-yaml";
|
|
||||||
|
|
||||||
import "@polymer/paper-input/paper-textarea";
|
|
||||||
|
|
||||||
import createCardElement from "../common/create-card-element";
|
|
||||||
import createErrorCardConfig from "../common/create-error-card-config";
|
|
||||||
import { HomeAssistant } from "../../../types";
|
|
||||||
import { LovelaceCard } from "../types";
|
|
||||||
import { ConfigValue } from "./types";
|
|
||||||
|
|
||||||
export class HuiYAMLCardPreview extends HTMLElement {
|
|
||||||
private _hass?: HomeAssistant;
|
|
||||||
|
|
||||||
set hass(value: HomeAssistant) {
|
|
||||||
this._hass = value;
|
|
||||||
if (this.lastChild) {
|
|
||||||
(this.lastChild as LovelaceCard).hass = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set value(configValue: ConfigValue) {
|
|
||||||
if (this.lastChild) {
|
|
||||||
this.removeChild(this.lastChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!configValue.value || configValue.value === "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let conf;
|
|
||||||
if (configValue.format === "yaml") {
|
|
||||||
try {
|
|
||||||
conf = yaml.safeLoad(configValue.value);
|
|
||||||
} catch (err) {
|
|
||||||
conf = createErrorCardConfig(`Invalid YAML: ${err.message}`, undefined);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
conf = configValue.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const element = createCardElement(conf);
|
|
||||||
|
|
||||||
if (this._hass) {
|
|
||||||
element.hass = this._hass;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.appendChild(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"hui-yaml-card-preview": HuiYAMLCardPreview;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("hui-yaml-card-preview", HuiYAMLCardPreview);
|
|
@ -1,37 +1,87 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
|
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { getCardConfig } from "../common/data";
|
||||||
|
|
||||||
export class HuiYAMLEditor extends LitElement {
|
export class HuiYAMLEditor extends LitElement {
|
||||||
public yaml?: string;
|
public cardId?: string;
|
||||||
|
protected hass?: HomeAssistant;
|
||||||
|
private _yaml?: string;
|
||||||
|
private _loading?: boolean;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return { _yaml: {}, cardId: {} };
|
||||||
yaml: {},
|
}
|
||||||
};
|
|
||||||
|
set yaml(yaml: string) {
|
||||||
|
if (yaml === undefined) {
|
||||||
|
this._loading = true;
|
||||||
|
this._loadConfig();
|
||||||
|
} else {
|
||||||
|
this._yaml = yaml;
|
||||||
|
if (this._loading) {
|
||||||
|
this._loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
${this.renderStyle()}
|
||||||
paper-textarea {
|
<paper-spinner
|
||||||
--paper-input-container-shared-input-style_-_font-family: monospace;
|
?active="${this._loading}"
|
||||||
}
|
alt="Loading"
|
||||||
</style>
|
class="center"
|
||||||
|
></paper-spinner>
|
||||||
<paper-textarea
|
<paper-textarea
|
||||||
max-rows="10"
|
max-rows="10"
|
||||||
value="${this.yaml}"
|
.value="${this._yaml}"
|
||||||
@value-changed="${this._valueChanged}"
|
@value-changed="${this._valueChanged}"
|
||||||
></paper-textarea>
|
></paper-textarea>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: MouseEvent): void {
|
private renderStyle(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
paper-textarea {
|
||||||
|
--paper-input-container-shared-input-style_-_font-family: monospace;
|
||||||
|
}
|
||||||
|
.center {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
paper-spinner {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
paper-spinner[active] {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadConfig(): Promise<void> {
|
||||||
|
if (!this.hass || !this.cardId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._yaml = await getCardConfig(this.hass, this.cardId);
|
||||||
|
if (this._loading) {
|
||||||
|
this._loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: Event): void {
|
||||||
const target = ev.target! as any;
|
const target = ev.target! as any;
|
||||||
this.yaml = target.value;
|
this._yaml = target.value;
|
||||||
fireEvent(this, "yaml-changed", { yaml: target.value });
|
fireEvent(this, "yaml-changed", {
|
||||||
|
yaml: target.value,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,8 +8,13 @@ export interface YamlChangedEvent extends Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigValue {
|
export interface ConfigValue {
|
||||||
format: "js" | "yaml";
|
format: "json" | "yaml";
|
||||||
value: string | LovelaceConfig;
|
value?: string | LovelaceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigError {
|
||||||
|
type: string;
|
||||||
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EntitiesEditorEvent {
|
export interface EntitiesEditorEvent {
|
||||||
|
22
src/panels/lovelace/editor/yaml-ext-schema.ts
Normal file
22
src/panels/lovelace/editor/yaml-ext-schema.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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,
|
||||||
|
]);
|
@ -147,7 +147,7 @@ class HUIView extends PolymerElement {
|
|||||||
|
|
||||||
const wrapper = document.createElement("hui-card-options");
|
const wrapper = document.createElement("hui-card-options");
|
||||||
wrapper.hass = this.hass;
|
wrapper.hass = this.hass;
|
||||||
wrapper.cardId = cardConfig.id;
|
wrapper.cardConfig = cardConfig;
|
||||||
wrapper.editMode = this.editMode;
|
wrapper.editMode = this.editMode;
|
||||||
wrapper.appendChild(element);
|
wrapper.appendChild(element);
|
||||||
elementsToAppend.push(wrapper);
|
elementsToAppend.push(wrapper);
|
||||||
|
@ -2,6 +2,7 @@ import { HomeAssistant } from "../../types";
|
|||||||
|
|
||||||
export interface LovelaceConfig {
|
export interface LovelaceConfig {
|
||||||
type: string;
|
type: string;
|
||||||
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceCard extends HTMLElement {
|
export interface LovelaceCard extends HTMLElement {
|
||||||
|
@ -765,6 +765,19 @@
|
|||||||
"clear_items": "Clear checked items",
|
"clear_items": "Clear checked items",
|
||||||
"add_item": "Add item"
|
"add_item": "Add item"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"editor": {
|
||||||
|
"edit": {
|
||||||
|
"header": "Card Configuration",
|
||||||
|
"save": "Save",
|
||||||
|
"toggle_editor": "Toggle Editor"
|
||||||
|
},
|
||||||
|
"migrate": {
|
||||||
|
"header": "Configuration Incompatible",
|
||||||
|
"para_no_id": "This element doesn't have an ID. Please add an ID to this element in 'ui-lovelace.yaml'.",
|
||||||
|
"para_migrate": "Home Assistant can add ID's to all your cards and views automatically for you by pressing the 'Migrate config' button.",
|
||||||
|
"migrate": "Migrate config"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mailbox": {
|
"mailbox": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user