mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-01 13:37:47 +00:00
Add card functionality (#2160)
* MVP add card * WIP * Added stub config * Little bit of cleanup * Add all card * At least it works now... * clean up and bug fixes * Fix for switching editor
This commit is contained in:
parent
57b5db4f43
commit
f461ad6d31
9
src/common/util/uid.ts
Normal file
9
src/common/util/uid.ts
Normal file
@ -0,0 +1,9 @@
|
||||
function s4() {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
|
||||
export function uid() {
|
||||
return s4() + s4() + s4() + s4() + s4();
|
||||
}
|
@ -33,12 +33,12 @@ export const migrateConfig = (hass: HomeAssistant): Promise<void> =>
|
||||
export const saveConfig = (
|
||||
hass: HomeAssistant,
|
||||
config: LovelaceConfig | string,
|
||||
configFormat: "json" | "yaml"
|
||||
format: "json" | "yaml"
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/config/save",
|
||||
config,
|
||||
format: configFormat,
|
||||
format,
|
||||
});
|
||||
|
||||
export const getCardConfig = (
|
||||
@ -54,13 +54,13 @@ export const updateCardConfig = (
|
||||
hass: HomeAssistant,
|
||||
cardId: string,
|
||||
config: LovelaceCardConfig | string,
|
||||
configFormat: "json" | "yaml"
|
||||
format: "json" | "yaml"
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/config/card/update",
|
||||
card_id: cardId,
|
||||
card_config: config,
|
||||
format: configFormat,
|
||||
format,
|
||||
});
|
||||
|
||||
export const deleteCard = (
|
||||
@ -71,3 +71,16 @@ export const deleteCard = (
|
||||
type: "lovelace/config/card/delete",
|
||||
card_id: cardId,
|
||||
});
|
||||
|
||||
export const addCard = (
|
||||
hass: HomeAssistant,
|
||||
viewId: string,
|
||||
config: LovelaceCardConfig | string,
|
||||
format: "json" | "yaml"
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/config/card/add",
|
||||
view_id: viewId,
|
||||
card_config: config,
|
||||
format,
|
||||
});
|
||||
|
@ -43,6 +43,11 @@ class HuiEntitiesCard extends hassLocalizeLitMixin(LitElement)
|
||||
await import("../editor/config-elements/hui-entities-card-editor");
|
||||
return document.createElement("hui-entities-card-editor");
|
||||
}
|
||||
|
||||
public static getStubConfig(): object {
|
||||
return { entities: [] };
|
||||
}
|
||||
|
||||
protected _hass?: HomeAssistant;
|
||||
protected _config?: Config;
|
||||
protected _configEntities?: ConfigEntity[];
|
||||
|
@ -47,6 +47,9 @@ export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
|
||||
await import("../editor/config-elements/hui-glance-card-editor");
|
||||
return document.createElement("hui-glance-card-editor");
|
||||
}
|
||||
public static getStubConfig(): object {
|
||||
return { entities: [] };
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
7
src/panels/lovelace/common/get-card-element-tag.ts
Normal file
7
src/panels/lovelace/common/get-card-element-tag.ts
Normal file
@ -0,0 +1,7 @@
|
||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||
|
||||
export function getCardElementTag(type: string): string {
|
||||
return type.startsWith(CUSTOM_TYPE_PREFIX)
|
||||
? type.substr(CUSTOM_TYPE_PREFIX.length)
|
||||
: `hui-${type}-card`;
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import {
|
||||
showEditCardDialog,
|
||||
registerEditCardDialog,
|
||||
} from "../editor/hui-dialog-edit-card";
|
||||
import { showEditCardDialog } from "../editor/hui-dialog-edit-card";
|
||||
|
||||
import { confDeleteCard } from "../editor/delete-card";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
@ -14,14 +11,14 @@ declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"show-edit-card": {
|
||||
cardConfig: LovelaceCardConfig;
|
||||
cardConfig?: LovelaceCardConfig;
|
||||
viewId?: string | number;
|
||||
add: boolean;
|
||||
reloadLovelace: () => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let registeredDialog = false;
|
||||
|
||||
export class HuiCardOptions extends LitElement {
|
||||
public cardConfig?: LovelaceCardConfig;
|
||||
protected hass?: HomeAssistant;
|
||||
@ -30,14 +27,6 @@ export class HuiCardOptions extends LitElement {
|
||||
return { hass: {} };
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (!registeredDialog) {
|
||||
registeredDialog = true;
|
||||
registerEditCardDialog(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<style>
|
||||
@ -72,6 +61,7 @@ export class HuiCardOptions extends LitElement {
|
||||
}
|
||||
showEditCardDialog(this, {
|
||||
cardConfig: this.cardConfig,
|
||||
add: false,
|
||||
reloadLovelace: () => fireEvent(this, "config-refresh"),
|
||||
});
|
||||
}
|
||||
|
@ -42,10 +42,6 @@ const cardConfigStruct = struct({
|
||||
|
||||
export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _configEntities?: ConfigEntity[];
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {}, _configEntities: {} };
|
||||
}
|
||||
@ -58,6 +54,10 @@ export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
return this._config!.theme || "Backend-selected";
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _configEntities?: ConfigEntity[];
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
|
||||
|
110
src/panels/lovelace/editor/hui-card-picker.ts
Normal file
110
src/panels/lovelace/editor/hui-card-picker.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import { html, LitElement } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { getCardElementTag } from "../common/get-card-element-tag";
|
||||
import { CardPickTarget } from "./types";
|
||||
import { uid } from "../../../common/util/uid";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"card-picked": {
|
||||
config: LovelaceCardConfig;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const cards = [
|
||||
{ name: "Alarm panel", type: "alarm-panel" },
|
||||
{ name: "Conditional", type: "conditional" },
|
||||
{ name: "Entities", type: "entities" },
|
||||
{ name: "Entity Button", type: "entity-button" },
|
||||
{ name: "Entity Filter", type: "entity-filter" },
|
||||
{ name: "Gauge", type: "gauge" },
|
||||
{ name: "Glance", type: "glance" },
|
||||
{ name: "History Graph", type: "history-graph" },
|
||||
{ name: "Horizontal Stack", type: "horizontal-graph" },
|
||||
{ name: "iFrame", type: "iframe" },
|
||||
{ name: "Light", type: "light" },
|
||||
{ name: "Map", type: "map" },
|
||||
{ name: "Markdown", type: "markdown" },
|
||||
{ name: "Media Control", type: "media-control" },
|
||||
{ name: "Picture", type: "picture" },
|
||||
{ name: "Picture Elements", type: "picture-elements" },
|
||||
{ name: "Picture Entity", type: "picture-entity" },
|
||||
{ name: "Picture Glance", type: "picture-glance" },
|
||||
{ name: "Plant Status", type: "plant-status" },
|
||||
{ name: "Sensor", type: "sensor" },
|
||||
{ name: "Shopping List", type: "shopping-list" },
|
||||
{ name: "Thermostat", type: "thermostat" },
|
||||
{ name: "Vertical Stack", type: "vertical-stack" },
|
||||
{ name: "Weather Forecast", type: "weather-forecast" },
|
||||
];
|
||||
|
||||
export class HuiCardPicker extends LitElement {
|
||||
protected hass?: HomeAssistant;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<h3>Pick the card you want to add:</h3>
|
||||
<div class="cards-container">
|
||||
${
|
||||
cards.map((card) => {
|
||||
return html`
|
||||
<paper-button
|
||||
raised
|
||||
@click="${this._cardPicked}"
|
||||
.type="${card.type}"
|
||||
>${card.name}</paper-button
|
||||
>
|
||||
`;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
.cards-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.cards-container paper-button {
|
||||
flex: 1 0 25%;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private _cardPicked(ev: Event): void {
|
||||
const type = (ev.currentTarget! as CardPickTarget).type;
|
||||
const tag = getCardElementTag(type);
|
||||
|
||||
const elClass = customElements.get(tag);
|
||||
let config: LovelaceCardConfig = { type, id: uid() };
|
||||
|
||||
if (elClass && elClass.getStubConfig) {
|
||||
const cardConfig = elClass.getStubConfig(this.hass);
|
||||
config = { ...config, ...cardConfig };
|
||||
}
|
||||
|
||||
fireEvent(this, "card-picked", {
|
||||
config,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-card-picker": HuiCardPicker;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-card-picker", HuiCardPicker);
|
@ -6,8 +6,7 @@ import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { ConfigError } from "./types";
|
||||
|
||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||
import { getCardElementTag } from "../common/get-card-element-tag";
|
||||
|
||||
export class HuiCardPreview extends HTMLElement {
|
||||
private _hass?: HomeAssistant;
|
||||
@ -39,9 +38,7 @@ export class HuiCardPreview extends HTMLElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const tag = configValue.type.startsWith(CUSTOM_TYPE_PREFIX)
|
||||
? configValue.type.substr(CUSTOM_TYPE_PREFIX.length)
|
||||
: `hui-${configValue.type}-card`;
|
||||
const tag = getCardElementTag(configValue.type);
|
||||
|
||||
if (tag.toUpperCase() === this._element.tagName) {
|
||||
this._element.setConfig(configValue);
|
||||
|
@ -19,15 +19,18 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
let registeredDialog = false;
|
||||
const dialogShowEvent = "show-edit-card";
|
||||
const dialogTag = "hui-dialog-edit-card";
|
||||
|
||||
export interface EditCardDialogParams {
|
||||
cardConfig: LovelaceCardConfig;
|
||||
cardConfig?: LovelaceCardConfig;
|
||||
viewId?: string | number;
|
||||
add: boolean;
|
||||
reloadLovelace: () => void;
|
||||
}
|
||||
|
||||
export const registerEditCardDialog = (element: HTMLElement) =>
|
||||
const registerEditCardDialog = (element: HTMLElement) =>
|
||||
fireEvent(element, "register-dialog", {
|
||||
dialogShowEvent,
|
||||
dialogTag,
|
||||
@ -37,7 +40,13 @@ export const registerEditCardDialog = (element: HTMLElement) =>
|
||||
export const showEditCardDialog = (
|
||||
element: HTMLElement,
|
||||
editCardDialogParams: EditCardDialogParams
|
||||
) => fireEvent(element, dialogShowEvent, editCardDialogParams);
|
||||
) => {
|
||||
if (!registeredDialog) {
|
||||
registeredDialog = true;
|
||||
registerEditCardDialog(element);
|
||||
}
|
||||
fireEvent(element, dialogShowEvent, editCardDialogParams);
|
||||
};
|
||||
|
||||
export class HuiDialogEditCard extends LitElement {
|
||||
protected hass?: HomeAssistant;
|
||||
@ -60,7 +69,12 @@ export class HuiDialogEditCard extends LitElement {
|
||||
if (!this._params) {
|
||||
return html``;
|
||||
}
|
||||
if (!this._params.cardConfig.id) {
|
||||
if (
|
||||
(!this._params.add &&
|
||||
this._params.cardConfig &&
|
||||
!this._params.cardConfig.id) ||
|
||||
(this._params.add && !this._params.viewId)
|
||||
) {
|
||||
return html`
|
||||
<hui-migrate-config
|
||||
.hass="${this.hass}"
|
||||
@ -70,13 +84,24 @@ export class HuiDialogEditCard extends LitElement {
|
||||
}
|
||||
return html`
|
||||
<hui-edit-card
|
||||
.cardConfig="${this._params.cardConfig}"
|
||||
.hass="${this.hass}"
|
||||
.viewId="${this._params.viewId}"
|
||||
.cardConfig="${this._params.cardConfig}"
|
||||
@reload-lovelace="${this._params.reloadLovelace}"
|
||||
@cancel-edit-card="${this._cancel}"
|
||||
>
|
||||
</hui-edit-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _cancel() {
|
||||
this._params = {
|
||||
add: false,
|
||||
reloadLovelace: () => {
|
||||
return;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import {
|
||||
html,
|
||||
LitElement,
|
||||
PropertyDeclarations,
|
||||
PropertyValues,
|
||||
} from "@polymer/lit-element";
|
||||
import { classMap } from "lit-html/directives/classMap";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import yaml from "js-yaml";
|
||||
@ -12,19 +17,30 @@ 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, LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import {
|
||||
addCard,
|
||||
updateCardConfig,
|
||||
LovelaceCardConfig,
|
||||
} from "../../../data/lovelace";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
|
||||
import "./hui-yaml-editor";
|
||||
import "./hui-card-picker";
|
||||
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 } from "../types";
|
||||
import { YamlChangedEvent, ConfigValue, ConfigError } from "./types";
|
||||
import {
|
||||
YamlChangedEvent,
|
||||
CardPickedEvent,
|
||||
ConfigValue,
|
||||
ConfigError,
|
||||
} from "./types";
|
||||
import { extYamlSchema } from "./yaml-ext-schema";
|
||||
import { EntityConfig } from "../entity-rows/types";
|
||||
import { getCardElementTag } from "../common/get-card-element-tag";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@ -37,30 +53,17 @@ declare global {
|
||||
"config-changed": {
|
||||
config: LovelaceCardConfig;
|
||||
};
|
||||
"cancel-edit-card": {};
|
||||
}
|
||||
}
|
||||
|
||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||
|
||||
export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
protected hass?: HomeAssistant;
|
||||
private _cardId?: string;
|
||||
private _originalConfig?: LovelaceCardConfig;
|
||||
private _configElement?: LovelaceCardEditor | null;
|
||||
private _uiEditor?: boolean;
|
||||
private _configValue?: ConfigValue;
|
||||
private _configState?: string;
|
||||
private _loading?: boolean;
|
||||
private _isToggleAvailable?: boolean;
|
||||
private _saving: boolean;
|
||||
private _errorMsg?: TemplateResult;
|
||||
private _cardType?: string;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
_hass: {},
|
||||
hass: {},
|
||||
cardConfig: {},
|
||||
viewId: {},
|
||||
_cardId: {},
|
||||
_originalConfig: {},
|
||||
_configElement: {},
|
||||
_configValue: {},
|
||||
_configState: {},
|
||||
@ -68,34 +71,9 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
_uiEditor: {},
|
||||
_saving: {},
|
||||
_loading: {},
|
||||
_isToggleAvailable: {},
|
||||
};
|
||||
}
|
||||
|
||||
protected constructor() {
|
||||
super();
|
||||
this._saving = false;
|
||||
}
|
||||
|
||||
set cardConfig(cardConfig: LovelaceCardConfig) {
|
||||
this._originalConfig = cardConfig;
|
||||
if (String(cardConfig.id) !== this._cardId) {
|
||||
this._configValue = { format: "yaml", value: undefined };
|
||||
this._configState = "OK";
|
||||
this._uiEditor = true;
|
||||
this._cardId = String(cardConfig.id);
|
||||
this._loadConfigElement(cardConfig);
|
||||
}
|
||||
}
|
||||
|
||||
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")!;
|
||||
}
|
||||
@ -104,9 +82,64 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
return this.shadowRoot!.querySelector("hui-card-preview")!;
|
||||
}
|
||||
|
||||
public cardConfig?: LovelaceCardConfig;
|
||||
public viewId?: string | number;
|
||||
protected hass?: HomeAssistant;
|
||||
private _cardId?: string;
|
||||
private _configElement?: LovelaceCardEditor | null;
|
||||
private _uiEditor?: boolean;
|
||||
private _configValue?: ConfigValue;
|
||||
private _configState?: string;
|
||||
private _loading?: boolean;
|
||||
private _saving: boolean;
|
||||
private _errorMsg?: TemplateResult;
|
||||
private _cardType?: string;
|
||||
|
||||
protected constructor() {
|
||||
super();
|
||||
this._saving = false;
|
||||
}
|
||||
|
||||
public async showDialog(): Promise<void> {
|
||||
// Wait till dialog is rendered.
|
||||
if (this._dialog == null) {
|
||||
await this.updateComplete;
|
||||
}
|
||||
this._dialog.open();
|
||||
}
|
||||
|
||||
protected async updated(changedProperties: PropertyValues): Promise<void> {
|
||||
super.updated(changedProperties);
|
||||
if (
|
||||
!changedProperties.has("cardConfig") &&
|
||||
!changedProperties.has("viewId")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._configValue = { format: "yaml", value: undefined };
|
||||
this._configState = "OK";
|
||||
this._uiEditor = true;
|
||||
this._errorMsg = undefined;
|
||||
this._configElement = undefined;
|
||||
|
||||
if (this.cardConfig && String(this.cardConfig.id) !== this._cardId) {
|
||||
this._loading = true;
|
||||
this._cardId = String(this.cardConfig.id);
|
||||
this._loadConfigElement(this.cardConfig);
|
||||
} else {
|
||||
this._cardId = undefined;
|
||||
}
|
||||
|
||||
if (this.viewId && !this.cardConfig) {
|
||||
this._resizeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
let content;
|
||||
if (!this._configElement !== undefined) {
|
||||
let preview;
|
||||
if (this._configElement !== undefined) {
|
||||
if (this._uiEditor) {
|
||||
content = html`
|
||||
<div class="element-editor">${this._configElement}</div>
|
||||
@ -121,6 +154,17 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
></hui-yaml-editor>
|
||||
`;
|
||||
}
|
||||
preview = html`
|
||||
<hr />
|
||||
<hui-card-preview .hass="${this.hass}"> </hui-card-preview>
|
||||
`;
|
||||
} else if (this.viewId && !this.cardConfig) {
|
||||
content = html`
|
||||
<hui-card-picker
|
||||
.hass="${this.hass}"
|
||||
@card-picked="${this._handleCardPicked}"
|
||||
></hui-card-picker>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
@ -142,16 +186,17 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
`
|
||||
: ""
|
||||
}
|
||||
${content}
|
||||
<hr />
|
||||
<hui-card-preview .hass="${this.hass}"></hui-card-preview>
|
||||
${content} ${preview}
|
||||
</paper-dialog-scrollable>
|
||||
${
|
||||
!this._loading
|
||||
? html`
|
||||
<div class="paper-dialog-buttons">
|
||||
<paper-button
|
||||
?disabled="${!this._isToggleAvailable}"
|
||||
?hidden="${!this._configValue || !this._configValue.value}"
|
||||
?disabled="${
|
||||
this._configElement === null || this._configState !== "OK"
|
||||
}"
|
||||
@click="${this._toggleEditor}"
|
||||
>${
|
||||
this.localize(
|
||||
@ -163,7 +208,8 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
>${this.localize("ui.common.cancel")}</paper-button
|
||||
>
|
||||
<paper-button
|
||||
?disabled="${this._saving}"
|
||||
?hidden="${!this._configValue || !this._configValue.value}"
|
||||
?disabled="${this._saving || this._configState !== "OK"}"
|
||||
@click="${this._save}"
|
||||
>
|
||||
<paper-spinner
|
||||
@ -229,39 +275,6 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
private async _toggleEditor(): Promise<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._uiEditor = !this._uiEditor;
|
||||
const cardConfig = this._configValue!.value! as LovelaceCardConfig;
|
||||
if (cardConfig.type !== this._cardType) {
|
||||
await this._loadConfigElement(cardConfig);
|
||||
this._cardType = cardConfig.type;
|
||||
}
|
||||
this._configElement.setConfig(cardConfig);
|
||||
}
|
||||
this._resizeDialog();
|
||||
}
|
||||
|
||||
private _save(): void {
|
||||
this._saving = true;
|
||||
this._updateConfigInBackend();
|
||||
@ -283,7 +296,9 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _closeDialog(): void {
|
||||
this._cardId = undefined;
|
||||
this.cardConfig = undefined;
|
||||
this.viewId = undefined;
|
||||
fireEvent(this, "cancel-edit-card");
|
||||
this._dialog.close();
|
||||
}
|
||||
|
||||
@ -301,21 +316,39 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
}
|
||||
|
||||
try {
|
||||
await updateCardConfig(
|
||||
this.hass!,
|
||||
this._cardId!,
|
||||
this._configValue!.value!,
|
||||
this._configValue!.format
|
||||
);
|
||||
if (this.viewId) {
|
||||
await addCard(
|
||||
this.hass!,
|
||||
String(this.viewId),
|
||||
this._configValue!.value!,
|
||||
this._configValue!.format
|
||||
);
|
||||
} else {
|
||||
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 async _handleCardPicked(ev: CardPickedEvent): Promise<void> {
|
||||
const succes = await this._loadConfigElement(ev.detail.config);
|
||||
if (!succes) {
|
||||
this._configValue = {
|
||||
format: "yaml",
|
||||
value: yaml.safeDump(ev.detail.config),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private _handleYamlChanged(ev: YamlChangedEvent): void {
|
||||
this._configValue = { format: "yaml", value: ev.detail.yaml };
|
||||
try {
|
||||
@ -324,11 +357,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
}) as LovelaceCardConfig;
|
||||
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",
|
||||
@ -365,8 +394,42 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
this._resizeDialog();
|
||||
}
|
||||
|
||||
private async _toggleEditor(): Promise<void> {
|
||||
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") {
|
||||
const yamlConfig = this._configValue!.value;
|
||||
const cardConfig = yaml.safeLoad(yamlConfig, {
|
||||
schema: extYamlSchema,
|
||||
}) as LovelaceCardConfig;
|
||||
this._uiEditor = !this._uiEditor;
|
||||
if (cardConfig.type !== this._cardType) {
|
||||
const succes = await this._loadConfigElement(cardConfig);
|
||||
if (!succes) {
|
||||
this._loadedDialog();
|
||||
}
|
||||
this._cardType = cardConfig.type;
|
||||
} else {
|
||||
this._configValue = {
|
||||
format: "json",
|
||||
value: cardConfig,
|
||||
};
|
||||
this._configElement.setConfig(cardConfig);
|
||||
}
|
||||
}
|
||||
this._resizeDialog();
|
||||
}
|
||||
|
||||
private _isConfigValid() {
|
||||
if (!this._cardId || !this._configValue || !this._configValue.value) {
|
||||
if (!this._configValue || !this._configValue.value) {
|
||||
return false;
|
||||
}
|
||||
if (this._configState === "OK") {
|
||||
@ -377,36 +440,36 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _isConfigChanged(): boolean {
|
||||
if (this.viewId) {
|
||||
return true;
|
||||
}
|
||||
const configValue =
|
||||
this._configValue!.format === "yaml"
|
||||
? yaml.safeDump(this._configValue!.value)
|
||||
: this._configValue!.value;
|
||||
return JSON.stringify(configValue) !== JSON.stringify(this._originalConfig);
|
||||
return JSON.stringify(configValue) !== JSON.stringify(this.cardConfig);
|
||||
}
|
||||
|
||||
private async _loadConfigElement(conf: LovelaceCardConfig): Promise<void> {
|
||||
private async _loadConfigElement(conf: LovelaceCardConfig): Promise<boolean> {
|
||||
if (!conf) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this._errorMsg = undefined;
|
||||
this._loading = true;
|
||||
this._configElement = undefined;
|
||||
this._isToggleAvailable = false;
|
||||
|
||||
const tag = conf.type.startsWith(CUSTOM_TYPE_PREFIX)
|
||||
? conf!.type.substr(CUSTOM_TYPE_PREFIX.length)
|
||||
: `hui-${conf!.type}-card`;
|
||||
const tag = getCardElementTag(conf.type);
|
||||
|
||||
const elClass = customElements.get(tag);
|
||||
let configElement;
|
||||
|
||||
try {
|
||||
if (elClass && elClass.getConfigElement) {
|
||||
configElement = await elClass.getConfigElement();
|
||||
} catch (err) {
|
||||
} else {
|
||||
this._uiEditor = false;
|
||||
this._configElement = null;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
@ -418,7 +481,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
`;
|
||||
this._uiEditor = false;
|
||||
this._configElement = null;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
configElement.hass = this.hass;
|
||||
@ -427,8 +490,9 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
);
|
||||
this._configValue = { format: "json", value: conf };
|
||||
this._configElement = configElement;
|
||||
this._isToggleAvailable = true;
|
||||
await this.updateComplete;
|
||||
this._updatePreview(conf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,12 @@ export interface YamlChangedEvent extends Event {
|
||||
};
|
||||
}
|
||||
|
||||
export interface CardPickedEvent extends Event {
|
||||
detail: {
|
||||
config: LovelaceCardConfig;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ConfigValue {
|
||||
format: "json" | "yaml";
|
||||
value?: string | LovelaceCardConfig;
|
||||
@ -30,3 +36,7 @@ export interface EditorTarget extends EventTarget {
|
||||
checked?: boolean;
|
||||
configValue?: string;
|
||||
}
|
||||
|
||||
export interface CardPickTarget extends EventTarget {
|
||||
type: string;
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "@polymer/paper-fab/paper-fab";
|
||||
import "../../components/entity/ha-state-label-badge";
|
||||
import "./components/hui-card-options";
|
||||
|
||||
import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
|
||||
|
||||
import EventsMixin from "../../mixins/events-mixin";
|
||||
import createCardElement from "./common/create-card-element";
|
||||
import { computeCardSize } from "./common/compute-card-size";
|
||||
import { showEditCardDialog } from "./editor/hui-dialog-edit-card";
|
||||
|
||||
class HUIView extends PolymerElement {
|
||||
class HUIView extends EventsMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
@ -44,6 +47,18 @@ class HUIView extends PolymerElement {
|
||||
margin: 4px 4px 8px;
|
||||
}
|
||||
|
||||
paper-fab {
|
||||
position: sticky;
|
||||
float: right;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
paper-fab[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
:host {
|
||||
padding-left: 0;
|
||||
@ -64,6 +79,13 @@ class HUIView extends PolymerElement {
|
||||
</style>
|
||||
<div id="badges"></div>
|
||||
<div id="columns"></div>
|
||||
<paper-fab
|
||||
hidden$="{{!editMode}}"
|
||||
elevated="2"
|
||||
icon="hass:plus"
|
||||
title="Add Card"
|
||||
on-click="_addCard"
|
||||
></paper-fab>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -93,6 +115,16 @@ class HUIView extends PolymerElement {
|
||||
this._badges = [];
|
||||
}
|
||||
|
||||
_addCard() {
|
||||
showEditCardDialog(this, {
|
||||
viewId: this.config.id,
|
||||
add: true,
|
||||
reloadLovelace: () => {
|
||||
this.fire("config-refresh");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_createBadges(config) {
|
||||
const root = this.$.badges;
|
||||
while (root.lastChild) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user