Various changes to card editor. (#3265)

* Various changes to card editor.

* Avoid crashing on bad yaml when creating a new card

* Address review comments

* Revert interface change

* Avoid config loops. Nicer error behavior.
This commit is contained in:
Thomas Lovén 2019-08-10 21:14:35 +02:00 committed by Paulus Schoutsen
parent ef3892de92
commit c15629b81b
5 changed files with 491 additions and 618 deletions

View File

@ -0,0 +1,281 @@
import {
html,
css,
LitElement,
TemplateResult,
CSSResult,
customElement,
property,
} from "lit-element";
import yaml from "js-yaml";
import "@material/mwc-button";
import { HomeAssistant } from "../../../../types";
import { LovelaceCardConfig } from "../../../../data/lovelace";
import { LovelaceCardEditor } from "../../types";
import { getCardElementTag } from "../../common/get-card-element-tag";
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 { fireEvent } from "../../../../common/dom/fire_event";
import { EntityConfig } from "../../entity-rows/types";
declare global {
interface HASSDomEvents {
"entities-changed": {
entities: EntityConfig[];
};
"config-changed": {
config: LovelaceCardConfig;
error?: string;
};
}
}
export interface UIConfigChangedEvent extends Event {
detail: {
config: LovelaceCardConfig;
};
}
@customElement("hui-card-editor")
export class HuiCardEditor extends LitElement {
@property() public hass?: HomeAssistant;
@property() private _yaml?: string;
@property() private _config?: LovelaceCardConfig;
@property() private _configElement?: LovelaceCardEditor;
@property() private _configElType?: string;
@property() private _GUImode: boolean = true;
// Error: Configuration broken - do not save
@property() private _error?: string;
// Warning: GUI editor can't handle configuration - ok to save
@property() private _warning?: string;
@property() private _loading: boolean = false;
public get yaml(): string {
return this._yaml || "";
}
public set yaml(_yaml: string) {
this._yaml = _yaml;
try {
this._config = yaml.safeLoad(this.yaml);
this._updateConfigElement();
setTimeout(() => {
if (this._yamlEditor) {
this._yamlEditor.codemirror.refresh();
}
}, 1);
this._error = undefined;
} catch (err) {
this._error = err.message;
}
fireEvent(this, "config-changed", {
config: this.value!,
error: this._error,
});
}
public get value(): LovelaceCardConfig | undefined {
return this._config;
}
public set value(config: LovelaceCardConfig | undefined) {
if (JSON.stringify(config) !== JSON.stringify(this._config || {})) {
this.yaml = yaml.safeDump(config);
}
}
public get hasError(): boolean {
return this._error !== undefined;
}
private get _yamlEditor(): HuiYamlEditor {
return this.shadowRoot!.querySelector("hui-yaml-editor")!;
}
public toggleMode() {
this._GUImode = !this._GUImode;
}
protected render(): TemplateResult {
return html`
<div class="wrapper">
${this._GUImode
? html`
<div class="gui-editor">
${this._loading
? html`
<paper-spinner
active
alt="Loading"
class="center margin-bot"
></paper-spinner>
`
: this._configElement}
</div>
`
: html`
<div class="yaml-editor">
<hui-yaml-editor
.hass=${this.hass}
.value=${this.yaml}
@yaml-changed=${this._handleYAMLChanged}
></hui-yaml-editor>
</div>
`}
${this._error
? html`
<div class="error">
${this._error}
</div>
`
: ""}
${this._warning
? html`
<div class="warning">
${this._warning}
</div>
`
: ""}
<div class="buttons">
<mwc-button
@click=${this.toggleMode}
?disabled=${this._warning || this._error}
?unelevated=${this._GUImode === false}
>
<ha-icon icon="mdi:code-braces"></ha-icon>
</mwc-button>
</div>
</div>
`;
}
protected updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has("_GUImode")) {
if (this._GUImode === false) {
// Refresh code editor when switching to yaml mode
this._yamlEditor.codemirror.refresh();
this._yamlEditor.codemirror.focus();
}
fireEvent(this as HTMLElement, "iron-resize");
}
}
private _handleUIConfigChanged(ev: UIConfigChangedEvent) {
ev.stopPropagation();
const config = ev.detail.config;
this.value = config;
}
private _handleYAMLChanged(ev) {
ev.stopPropagation();
const newYaml = ev.detail.value;
if (newYaml !== this.yaml) {
this.yaml = newYaml;
}
}
private async _updateConfigElement(): Promise<void> {
if (!this.value) {
return;
}
const cardType = this.value.type;
let configElement = this._configElement;
try {
this._error = undefined;
this._warning = undefined;
if (this._configElType !== cardType) {
// If the card type has changed, we need to load a new GUI editor
if (!this.value.type) {
throw new Error("No card type defined");
}
const tag = getCardElementTag(cardType);
// Check if the card type exists
const elClass = customElements.get(tag);
if (!elClass) {
throw new Error(`Unknown card type encountered: ${cardType}.`);
}
this._loading = true;
// Check if a GUI editor exists
if (elClass && elClass.getConfigElement) {
configElement = await elClass.getConfigElement();
} else {
configElement = undefined;
throw Error(`WARNING: No GUI editor available for: ${cardType}`);
}
this._configElement = configElement;
this._configElType = cardType;
}
// Setup GUI editor and check that it can handle the current config
try {
this._configElement!.setConfig(this.value);
} catch (err) {
throw Error(`WARNING: ${err.message}`);
}
// Perform final setup
this._configElement!.hass = this.hass;
this._configElement!.addEventListener("config-changed", (ev) =>
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
);
return;
} catch (err) {
if (err.message.startsWith("WARNING:")) {
this._warning = err.message.substr(8);
} else {
this._error = err;
}
this._GUImode = false;
} finally {
this._loading = false;
fireEvent(this, "iron-resize");
}
}
static get styles(): CSSResult {
return css`
:host {
display: flex;
}
.wrapper {
width: 100%;
}
.gui-editor,
.yaml-editor {
padding: 8px 0px;
}
.error {
color: #ef5350;
}
.warning {
color: #ffa726;
}
.buttons {
text-align: right;
padding: 8px 0px;
}
paper-spinner {
display: block;
margin: auto;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-card-editor": HuiCardEditor;
}
}

View File

@ -12,6 +12,7 @@ import { HomeAssistant } from "../../../../types";
import { LovelaceCardConfig } from "../../../../data/lovelace";
import { getCardElementTag } from "../../common/get-card-element-tag";
import { CardPickTarget } from "../types";
import { fireEvent } from "../../../../common/dom/fire_event";
const cards = [
{ name: "Alarm panel", type: "alarm-panel" },
@ -60,6 +61,9 @@ export class HuiCardPicker extends LitElement {
`;
})}
</div>
<div class="cards-container">
<mwc-button @click="${this._manualPicked}">MANUAL CARD</mwc-button>
</div>
`;
}
@ -85,6 +89,12 @@ export class HuiCardPicker extends LitElement {
];
}
private _manualPicked(): void {
fireEvent(this, "config-changed", {
config: { type: "" },
});
}
private _cardPicked(ev: Event): void {
const type = (ev.currentTarget! as CardPickTarget).type;
const tag = getCardElementTag(type);
@ -97,7 +107,7 @@ export class HuiCardPicker extends LitElement {
config = { ...config, ...cardConfig };
}
this.cardPicked!(config);
fireEvent(this, "config-changed", { config });
}
}

View File

@ -1,7 +1,9 @@
import {
css,
html,
LitElement,
TemplateResult,
CSSResultArray,
customElement,
property,
} from "lit-element";
@ -9,11 +11,17 @@ import {
import { HomeAssistant } from "../../../../types";
import { HASSDomEvent } from "../../../../common/dom/fire_event";
import { LovelaceCardConfig } from "../../../../data/lovelace";
import "./hui-edit-card";
import "./hui-dialog-pick-card";
import "./hui-card-editor";
// tslint:disable-next-line
import { HuiCardEditor } from "./hui-card-editor";
import "./hui-card-preview";
import "./hui-card-picker";
import { EditCardDialogParams } from "./show-edit-card-dialog";
import { addCard, replaceCard } from "../config-util";
import "../../../../components/dialog/ha-paper-dialog";
import { haStyleDialog } from "../../../../resources/styles";
declare global {
// for fire event
interface HASSDomEvents {
@ -33,72 +41,227 @@ export class HuiDialogEditCard extends LitElement {
@property() private _cardConfig?: LovelaceCardConfig;
@property() private _newCard?: boolean;
constructor() {
super();
this._cardPicked = this._cardPicked.bind(this);
this._cancel = this._cancel.bind(this);
this._save = this._save.bind(this);
}
@property() private _saving: boolean = false;
@property() private _error?: string;
public async showDialog(params: EditCardDialogParams): Promise<void> {
this._params = params;
const [view, card] = params.path;
this._newCard = card !== undefined ? false : true;
this._cardConfig =
card !== undefined
? params.lovelace.config.views[view].cards![card]
: undefined;
}
private get _cardEditorEl(): HuiCardEditor | null {
return this.shadowRoot!.querySelector("hui-card-editor");
}
protected render(): TemplateResult | void {
if (!this._params) {
return html``;
}
if (!this._cardConfig) {
// Card picker
return html`
<hui-dialog-pick-card
.hass="${this.hass}"
.cardPicked="${this._cardPicked}"
.closeDialog="${this._cancel}"
></hui-dialog-pick-card>
`;
}
return html`
<hui-edit-card
.hass="${this.hass}"
.lovelace="${this._params.lovelace}"
.cardConfig="${this._cardConfig}"
.closeDialog="${this._cancel}"
.saveCard="${this._save}"
.newCard="${this._newCard}"
>
</hui-edit-card>
<ha-paper-dialog with-backdrop opened modal>
<h2>
${this.hass!.localize("ui.panel.lovelace.editor.edit_card.header")}
</h2>
<paper-dialog-scrollable>
${this._cardConfig === undefined
? html`
<hui-card-picker
.hass="${this.hass}"
@config-changed="${this._handleConfigChanged}"
></hui-card-picker>
`
: html`
<div class="content">
<div class="element-editor">
<hui-card-editor
.hass="${this.hass}"
.value="${this._cardConfig}"
@config-changed="${this._handleConfigChanged}"
></hui-card-editor>
</div>
<div class="element-preview">
<hui-card-preview
.hass="${this.hass}"
.config="${this._cardConfig}"
class=${this._error ? "blur" : ""}
></hui-card-preview>
${this._error
? html`
<paper-spinner
active
alt="Can't update card"
></paper-spinner>
`
: ``}
</div>
</div>
`}
</paper-dialog-scrollable>
<div class="paper-dialog-buttons">
<mwc-button @click="${this._close}">
${this.hass!.localize("ui.common.cancel")}
</mwc-button>
<mwc-button
?disabled="${!this._canSave || this._saving}"
@click="${this._save}"
>
${this._saving
? html`
<paper-spinner active alt="Saving"></paper-spinner>
`
: this.hass!.localize("ui.common.save")}
</mwc-button>
</div>
</ha-paper-dialog>
`;
}
private _cardPicked(cardConf: LovelaceCardConfig): void {
this._cardConfig = cardConf;
static get styles(): CSSResultArray {
return [
haStyleDialog,
css`
:host {
--code-mirror-max-height: calc(100vh - 176px);
}
@media all and (max-width: 450px), all and (max-height: 500px) {
/* overrule the ha-style-dialog max-height on small screens */
ha-paper-dialog {
max-height: 100%;
height: 100%;
}
}
@media all and (min-width: 660px) {
ha-paper-dialog {
width: 845px;
}
}
ha-paper-dialog {
max-width: 845px;
}
.center {
margin-left: auto;
margin-right: auto;
}
.content {
display: flex;
flex-direction: column;
margin: 0 -10px;
}
.content hui-card-preview {
margin: 4px auto;
max-width: 390px;
}
.content .element-editor {
margin: 0 10px;
}
@media (min-width: 1200px) {
ha-paper-dialog {
max-width: none;
width: 1000px;
}
.content {
flex-direction: row;
}
.content > * {
flex-basis: 0;
flex-grow: 1;
flex-shrink: 1;
min-width: 0;
}
.content hui-card-preview {
padding: 8px 0;
margin: auto 10px;
max-width: 500px;
}
}
mwc-button paper-spinner {
width: 14px;
height: 14px;
margin-right: 20px;
}
.hidden {
display: none;
}
.element-editor {
margin-bottom: 8px;
}
.blur {
filter: blur(2px) grayscale(100%);
}
.element-preview {
position: relative;
}
.element-preview paper-spinner {
top: 50%;
left: 50%;
position: absolute;
z-index: 10;
}
hui-card-preview {
padding-top: 8px;
margin-bottom: 4px;
display: block;
width: 100%;
}
`,
];
}
private _cancel(): void {
private _handleConfigChanged(ev) {
this._cardConfig = ev.detail.config;
this._error = ev.detail.error;
}
private _close(): void {
this._params = undefined;
this._cardConfig = undefined;
this._error = undefined;
}
private async _save(cardConf: LovelaceCardConfig): Promise<void> {
private get _canSave(): boolean {
if (this._saving) {
return false;
}
if (this._cardConfig === undefined) {
return false;
}
if (this._cardEditorEl && this._cardEditorEl.hasError) {
return false;
}
return true;
}
private async _save(): Promise<void> {
const lovelace = this._params!.lovelace;
this._saving = true;
await lovelace.saveConfig(
this._params!.path.length === 1
? addCard(lovelace.config, this._params!.path as [number], cardConf)
? addCard(
lovelace.config,
this._params!.path as [number],
this._cardConfig!
)
: replaceCard(
lovelace.config,
this._params!.path as [number, number],
cardConf
this._cardConfig!
)
);
this._saving = false;
this._close();
}
}

View File

@ -1,88 +0,0 @@
import {
html,
css,
LitElement,
TemplateResult,
CSSResult,
customElement,
} from "lit-element";
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
import "../../../../components/dialog/ha-paper-dialog";
import { haStyleDialog } from "../../../../resources/styles";
import "./hui-card-picker";
import { HomeAssistant } from "../../../../types";
import { LovelaceCardConfig } from "../../../../data/lovelace";
@customElement("hui-dialog-pick-card")
export class HuiDialogPickCard extends LitElement {
public hass?: HomeAssistant;
public cardPicked?: (cardConf: LovelaceCardConfig) => void;
public closeDialog?: () => void;
protected render(): TemplateResult | void {
return html`
<ha-paper-dialog
with-backdrop
opened
@opened-changed="${this._openedChanged}"
>
<h2>
${this.hass!.localize("ui.panel.lovelace.editor.edit_card.header")}
</h2>
<paper-dialog-scrollable>
<hui-card-picker
.hass="${this.hass}"
.cardPicked="${this.cardPicked}"
></hui-card-picker>
</paper-dialog-scrollable>
<div class="paper-dialog-buttons">
<mwc-button @click="${this._skipPick}">MANUAL CARD</mwc-button>
</div>
</ha-paper-dialog>
`;
}
private _openedChanged(ev): void {
if (!ev.detail.value) {
this.closeDialog!();
}
}
private _skipPick() {
this.cardPicked!({ type: "" });
}
static get styles(): CSSResult[] {
return [
haStyleDialog,
css`
@media all and (max-width: 450px), all and (max-height: 500px) {
/* overrule the ha-style-dialog max-height on small screens */
ha-paper-dialog {
max-height: 100%;
height: 100%;
}
}
@media all and (min-width: 660px) {
ha-paper-dialog {
width: 650px;
}
}
ha-paper-dialog {
max-width: 650px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-dialog-pick-card": HuiDialogPickCard;
}
}

View File

@ -1,493 +0,0 @@
import {
html,
css,
LitElement,
PropertyValues,
TemplateResult,
CSSResult,
customElement,
property,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import yaml from "js-yaml";
import { haStyleDialog } from "../../../../resources/styles";
import "@polymer/paper-spinner/paper-spinner";
import "@polymer/paper-dialog/paper-dialog";
import "../../../../components/dialog/ha-paper-dialog";
// This is not a duplicate import, one is for types, one is for element.
// tslint:disable-next-line
import { HaPaperDialog } from "../../../../components/dialog/ha-paper-dialog";
import "@material/mwc-button";
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
import { HomeAssistant } from "../../../../types";
import { LovelaceCardConfig } from "../../../../data/lovelace";
import { fireEvent } from "../../../../common/dom/fire_event";
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 { ConfigError } from "../types";
import { EntityConfig } from "../../entity-rows/types";
import { getCardElementTag } from "../../common/get-card-element-tag";
import { afterNextRender } from "../../../../common/util/render-status";
declare global {
interface HASSDomEvents {
"entities-changed": {
entities: EntityConfig[];
};
"config-changed": {
config: LovelaceCardConfig;
};
}
}
@customElement("hui-edit-card")
export class HuiEditCard extends LitElement {
@property() public hass?: HomeAssistant;
@property() public cardConfig?: LovelaceCardConfig;
public lovelace?: Lovelace;
public closeDialog?: () => void;
public saveCard?: (cardConf: LovelaceCardConfig) => Promise<void>;
public newCard?: boolean;
@property() private _configElement?: LovelaceCardEditor | null;
@property() private _uiEditor?: boolean;
@property() private _cardConfig?: LovelaceCardConfig;
@property() private _configState?: string;
@property() private _loading?: boolean;
@property() private _saving: boolean;
@property() private _errorMsg?: TemplateResult;
private get _dialog(): HaPaperDialog {
return this.shadowRoot!.querySelector("ha-paper-dialog")!;
}
private get _previewEl(): HuiCardPreview {
return this.shadowRoot!.querySelector("hui-card-preview")!;
}
// tslint:disable-next-line
private __cardYaml: string | undefined;
private get _cardYaml(): string | undefined {
if (!this.__cardYaml) {
this.__cardYaml = yaml.safeDump(this._cardConfig);
}
return this.__cardYaml;
}
private set _cardYaml(yml: string | undefined) {
this.__cardYaml = yml;
}
public constructor() {
super();
this._saving = false;
}
protected updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
if (!changedProperties.has("cardConfig")) {
return;
}
this._cardConfig = undefined;
this._cardYaml = undefined;
this._configState = "OK";
this._uiEditor = true;
this._errorMsg = undefined;
this._configElement = undefined;
this._loading = true;
this._loadConfigElement(this.cardConfig!);
}
protected render(): TemplateResult | void {
let content;
let preview;
if (this._configElement !== undefined) {
content = html`
<div class="element-editor">
${this._uiEditor
? this._configElement
: html`
<hui-yaml-editor
.hass="${this.hass}"
.value="${this._cardYaml}"
@yaml-changed="${this._handleYamlChanged}"
@yaml-save="${this._save}"
></hui-yaml-editor>
`}
</div>
`;
preview = html`
<hui-card-preview .hass="${this.hass}"> </hui-card-preview>
`;
}
return html`
<ha-paper-dialog
with-backdrop
opened
modal
@opened-changed="${this._openedChanged}"
>
<h2>
${this.hass!.localize("ui.panel.lovelace.editor.edit_card.header")}
</h2>
<paper-spinner
?active="${this._loading}"
alt="Loading"
class="center margin-bot"
></paper-spinner>
<paper-dialog-scrollable
class="${classMap({ hidden: this._loading! })}"
>
${this._errorMsg
? html`
<div class="error">${this._errorMsg}</div>
`
: ""}
<div class="content">${content}${preview}</div>
</paper-dialog-scrollable>
${!this._loading
? html`
<div class="paper-dialog-buttons">
<mwc-button
class="toggle-button"
?disabled="${this._configElement === null ||
this._configState !== "OK"}"
@click="${this._toggleEditor}"
>${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.toggle_editor"
)}</mwc-button
>
<mwc-button @click="${this.closeDialog}"
>${this.hass!.localize("ui.common.cancel")}</mwc-button
>
<mwc-button
?disabled="${this._saving || this._configState !== "OK"}"
@click="${this._save}"
>
<paper-spinner
?active="${this._saving}"
alt="Saving"
></paper-spinner>
${this.hass!.localize("ui.common.save")}
</mwc-button>
</div>
`
: ""}
</ha-paper-dialog>
`;
}
private async _loadedDialog(): Promise<void> {
await this.updateComplete;
this._loading = false;
this._resizeDialog();
if (!this._uiEditor) {
afterNextRender(() => {
this.yamlEditor.codemirror.refresh();
this._resizeDialog();
this.yamlEditor.codemirror.focus();
});
}
}
private async _resizeDialog(): Promise<void> {
await this.updateComplete;
fireEvent(this._dialog as HTMLElement, "iron-resize");
}
private async _save(): Promise<void> {
if (!this._isConfigValid()) {
alert("Your config is not valid, please fix your config before saving.");
return;
}
if (!this._isConfigChanged()) {
this.closeDialog!();
return;
}
this._saving = true;
try {
await this.saveCard!(this._cardConfig!);
this._cardYaml = undefined;
this.closeDialog!();
} catch (err) {
alert(`Saving failed: ${err.message}`);
} finally {
this._saving = false;
}
}
private _handleYamlChanged(ev: CustomEvent): void {
try {
this._cardConfig = yaml.safeLoad(ev.detail.value);
this._updatePreview(this._cardConfig!);
this._configState = "OK";
} catch (err) {
this._configState = "YAML_ERROR";
this._setPreviewError({
type: "YAML Error",
message: err,
});
}
}
private _handleUIConfigChanged(value: LovelaceCardConfig): void {
this._cardConfig = value;
this._updatePreview(value);
}
private async _updatePreview(config: LovelaceCardConfig): Promise<void> {
await this.updateComplete;
if (!this._previewEl) {
return;
}
this._previewEl.config = config;
if (this._loading) {
this._loadedDialog();
} else {
this._resizeDialog();
}
}
private _setPreviewError(error: ConfigError): void {
if (!this._previewEl) {
return;
}
this._previewEl.error = error;
this._resizeDialog();
}
private async _toggleEditor(): Promise<void> {
this._cardYaml = undefined;
if (this._uiEditor) {
this._uiEditor = false;
} else if (this._configElement) {
const success = await this._loadConfigElement(this._cardConfig!);
if (!success) {
this._loadedDialog();
} else {
this._uiEditor = true;
this._configElement.setConfig(this._cardConfig!);
}
}
this._resizeDialog();
}
private _isConfigValid(): boolean {
if (!this._cardConfig) {
return false;
}
if (this._configState === "OK") {
return true;
} else {
return false;
}
}
private _isConfigChanged(): boolean {
if (this.newCard) {
return true;
}
return JSON.stringify(this._cardConfig) !== JSON.stringify(this.cardConfig);
}
private async _loadConfigElement(conf: LovelaceCardConfig): Promise<boolean> {
if (!conf) {
return false;
}
this._errorMsg = undefined;
this._loading = true;
this._configElement = undefined;
const tag = getCardElementTag(conf.type);
const elClass = customElements.get(tag);
let configElement;
this._cardConfig = conf;
if (elClass && elClass.getConfigElement) {
configElement = await elClass.getConfigElement();
} else {
this._updatePreview(conf);
this._uiEditor = false;
this._configElement = null;
return false;
}
try {
configElement.setConfig(conf);
} catch (err) {
this._errorMsg = html`
Your config is not supported by the UI editor:<br /><b>${err.message}</b
><br />Falling back to YAML editor.
`;
this._updatePreview(conf);
this._uiEditor = false;
this._configElement = null;
return false;
}
configElement.hass = this.hass;
configElement.addEventListener("config-changed", (ev) =>
this._handleUIConfigChanged(ev.detail.config)
);
this._configElement = configElement;
await this.updateComplete;
this._updatePreview(conf);
return true;
}
private _openedChanged(ev): void {
if (!ev.detail.value) {
this.closeDialog!();
}
}
private get yamlEditor(): HuiYamlEditor {
return this.shadowRoot!.querySelector("hui-yaml-editor")!;
}
static get styles(): CSSResult[] {
return [
haStyleDialog,
css`
:host {
--code-mirror-max-height: calc(100vh - 176px);
}
@media all and (max-width: 450px), all and (max-height: 500px) {
/* overrule the ha-style-dialog max-height on small screens */
ha-paper-dialog {
max-height: 100%;
height: 100%;
}
}
@media all and (min-width: 660px) {
ha-paper-dialog {
width: 845px;
}
}
ha-paper-dialog {
max-width: 845px;
}
.center {
margin-left: auto;
margin-right: auto;
}
.content {
display: flex;
flex-direction: column;
margin: 0 -10px;
}
.content hui-card-preview {
margin-top: 16px;
margin: 0 auto;
max-width: 390px;
}
.content .element-editor {
margin: 0 10px;
}
@media (min-width: 1200px) {
ha-paper-dialog {
max-width: none;
width: 1000px;
}
.content {
flex-direction: row;
}
.content > * {
flex-basis: 0;
flex-grow: 1;
flex-shrink: 1;
min-width: 0;
}
.content hui-card-preview {
padding-top: 0;
margin: 0 10px;
max-width: 490px;
}
}
.margin-bot {
margin-bottom: 24px;
}
mwc-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: 8px;
}
.error {
color: #ef5350;
border-bottom: 1px solid #ef5350;
}
hui-card-preview {
padding-top: 8px;
margin-bottom: 4px;
display: block;
}
.toggle-button {
margin-right: auto;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-edit-card": HuiEditCard;
}
}