mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-02 14:07:55 +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 = (
|
export const saveConfig = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config: LovelaceConfig | string,
|
config: LovelaceConfig | string,
|
||||||
configFormat: "json" | "yaml"
|
format: "json" | "yaml"
|
||||||
): Promise<void> =>
|
): Promise<void> =>
|
||||||
hass.callWS({
|
hass.callWS({
|
||||||
type: "lovelace/config/save",
|
type: "lovelace/config/save",
|
||||||
config,
|
config,
|
||||||
format: configFormat,
|
format,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getCardConfig = (
|
export const getCardConfig = (
|
||||||
@ -54,13 +54,13 @@ export const updateCardConfig = (
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
cardId: string,
|
cardId: string,
|
||||||
config: LovelaceCardConfig | string,
|
config: LovelaceCardConfig | string,
|
||||||
configFormat: "json" | "yaml"
|
format: "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,
|
format,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const deleteCard = (
|
export const deleteCard = (
|
||||||
@ -71,3 +71,16 @@ export const deleteCard = (
|
|||||||
type: "lovelace/config/card/delete",
|
type: "lovelace/config/card/delete",
|
||||||
card_id: cardId,
|
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");
|
await import("../editor/config-elements/hui-entities-card-editor");
|
||||||
return document.createElement("hui-entities-card-editor");
|
return document.createElement("hui-entities-card-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getStubConfig(): object {
|
||||||
|
return { entities: [] };
|
||||||
|
}
|
||||||
|
|
||||||
protected _hass?: HomeAssistant;
|
protected _hass?: HomeAssistant;
|
||||||
protected _config?: Config;
|
protected _config?: Config;
|
||||||
protected _configEntities?: ConfigEntity[];
|
protected _configEntities?: ConfigEntity[];
|
||||||
|
@ -47,6 +47,9 @@ export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
await import("../editor/config-elements/hui-glance-card-editor");
|
await import("../editor/config-elements/hui-glance-card-editor");
|
||||||
return document.createElement("hui-glance-card-editor");
|
return document.createElement("hui-glance-card-editor");
|
||||||
}
|
}
|
||||||
|
public static getStubConfig(): object {
|
||||||
|
return { entities: [] };
|
||||||
|
}
|
||||||
|
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: Config;
|
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 { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
|
import "@polymer/paper-button/paper-button";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import {
|
import { showEditCardDialog } from "../editor/hui-dialog-edit-card";
|
||||||
showEditCardDialog,
|
|
||||||
registerEditCardDialog,
|
|
||||||
} from "../editor/hui-dialog-edit-card";
|
|
||||||
|
|
||||||
import { confDeleteCard } from "../editor/delete-card";
|
import { confDeleteCard } from "../editor/delete-card";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
@ -14,14 +11,14 @@ declare global {
|
|||||||
// for fire event
|
// for fire event
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"show-edit-card": {
|
"show-edit-card": {
|
||||||
cardConfig: LovelaceCardConfig;
|
cardConfig?: LovelaceCardConfig;
|
||||||
|
viewId?: string | number;
|
||||||
|
add: boolean;
|
||||||
reloadLovelace: () => void;
|
reloadLovelace: () => void;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let registeredDialog = false;
|
|
||||||
|
|
||||||
export class HuiCardOptions extends LitElement {
|
export class HuiCardOptions extends LitElement {
|
||||||
public cardConfig?: LovelaceCardConfig;
|
public cardConfig?: LovelaceCardConfig;
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
@ -30,14 +27,6 @@ export class HuiCardOptions extends LitElement {
|
|||||||
return { hass: {} };
|
return { hass: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
public connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
if (!registeredDialog) {
|
|
||||||
registeredDialog = true;
|
|
||||||
registerEditCardDialog(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
@ -72,6 +61,7 @@ export class HuiCardOptions extends LitElement {
|
|||||||
}
|
}
|
||||||
showEditCardDialog(this, {
|
showEditCardDialog(this, {
|
||||||
cardConfig: this.cardConfig,
|
cardConfig: this.cardConfig,
|
||||||
|
add: false,
|
||||||
reloadLovelace: () => fireEvent(this, "config-refresh"),
|
reloadLovelace: () => fireEvent(this, "config-refresh"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,6 @@ const cardConfigStruct = struct({
|
|||||||
|
|
||||||
export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||||
implements LovelaceCardEditor {
|
implements LovelaceCardEditor {
|
||||||
public hass?: HomeAssistant;
|
|
||||||
private _config?: Config;
|
|
||||||
private _configEntities?: ConfigEntity[];
|
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return { hass: {}, _config: {}, _configEntities: {} };
|
return { hass: {}, _config: {}, _configEntities: {} };
|
||||||
}
|
}
|
||||||
@ -58,6 +54,10 @@ export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
|||||||
return this._config!.theme || "Backend-selected";
|
return this._config!.theme || "Backend-selected";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hass?: HomeAssistant;
|
||||||
|
private _config?: Config;
|
||||||
|
private _configEntities?: ConfigEntity[];
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
config = cardConfigStruct(config);
|
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 { LovelaceCardConfig } from "../../../data/lovelace";
|
||||||
import { LovelaceCard } from "../types";
|
import { LovelaceCard } from "../types";
|
||||||
import { ConfigError } from "./types";
|
import { ConfigError } from "./types";
|
||||||
|
import { getCardElementTag } from "../common/get-card-element-tag";
|
||||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
|
||||||
|
|
||||||
export class HuiCardPreview extends HTMLElement {
|
export class HuiCardPreview extends HTMLElement {
|
||||||
private _hass?: HomeAssistant;
|
private _hass?: HomeAssistant;
|
||||||
@ -39,9 +38,7 @@ export class HuiCardPreview extends HTMLElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tag = configValue.type.startsWith(CUSTOM_TYPE_PREFIX)
|
const tag = getCardElementTag(configValue.type);
|
||||||
? configValue.type.substr(CUSTOM_TYPE_PREFIX.length)
|
|
||||||
: `hui-${configValue.type}-card`;
|
|
||||||
|
|
||||||
if (tag.toUpperCase() === this._element.tagName) {
|
if (tag.toUpperCase() === this._element.tagName) {
|
||||||
this._element.setConfig(configValue);
|
this._element.setConfig(configValue);
|
||||||
|
@ -19,15 +19,18 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let registeredDialog = false;
|
||||||
const dialogShowEvent = "show-edit-card";
|
const dialogShowEvent = "show-edit-card";
|
||||||
const dialogTag = "hui-dialog-edit-card";
|
const dialogTag = "hui-dialog-edit-card";
|
||||||
|
|
||||||
export interface EditCardDialogParams {
|
export interface EditCardDialogParams {
|
||||||
cardConfig: LovelaceCardConfig;
|
cardConfig?: LovelaceCardConfig;
|
||||||
|
viewId?: string | number;
|
||||||
|
add: boolean;
|
||||||
reloadLovelace: () => void;
|
reloadLovelace: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const registerEditCardDialog = (element: HTMLElement) =>
|
const registerEditCardDialog = (element: HTMLElement) =>
|
||||||
fireEvent(element, "register-dialog", {
|
fireEvent(element, "register-dialog", {
|
||||||
dialogShowEvent,
|
dialogShowEvent,
|
||||||
dialogTag,
|
dialogTag,
|
||||||
@ -37,7 +40,13 @@ export const registerEditCardDialog = (element: HTMLElement) =>
|
|||||||
export const showEditCardDialog = (
|
export const showEditCardDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
editCardDialogParams: EditCardDialogParams
|
editCardDialogParams: EditCardDialogParams
|
||||||
) => fireEvent(element, dialogShowEvent, editCardDialogParams);
|
) => {
|
||||||
|
if (!registeredDialog) {
|
||||||
|
registeredDialog = true;
|
||||||
|
registerEditCardDialog(element);
|
||||||
|
}
|
||||||
|
fireEvent(element, dialogShowEvent, editCardDialogParams);
|
||||||
|
};
|
||||||
|
|
||||||
export class HuiDialogEditCard extends LitElement {
|
export class HuiDialogEditCard extends LitElement {
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
@ -60,7 +69,12 @@ export class HuiDialogEditCard extends LitElement {
|
|||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return html``;
|
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`
|
return html`
|
||||||
<hui-migrate-config
|
<hui-migrate-config
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
@ -70,13 +84,24 @@ export class HuiDialogEditCard extends LitElement {
|
|||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<hui-edit-card
|
<hui-edit-card
|
||||||
.cardConfig="${this._params.cardConfig}"
|
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
|
.viewId="${this._params.viewId}"
|
||||||
|
.cardConfig="${this._params.cardConfig}"
|
||||||
@reload-lovelace="${this._params.reloadLovelace}"
|
@reload-lovelace="${this._params.reloadLovelace}"
|
||||||
|
@cancel-edit-card="${this._cancel}"
|
||||||
>
|
>
|
||||||
</hui-edit-card>
|
</hui-edit-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _cancel() {
|
||||||
|
this._params = {
|
||||||
|
add: false,
|
||||||
|
reloadLovelace: () => {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
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 { classMap } from "lit-html/directives/classMap";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
@ -12,19 +17,30 @@ import "@polymer/paper-button/paper-button";
|
|||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
import { HomeAssistant } from "../../../types";
|
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 { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
|
|
||||||
import "./hui-yaml-editor";
|
import "./hui-yaml-editor";
|
||||||
|
import "./hui-card-picker";
|
||||||
import "./hui-card-preview";
|
import "./hui-card-preview";
|
||||||
// This is not a duplicate import, one is for types, one is for element.
|
// This is not a duplicate import, one is for types, one is for element.
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
import { HuiCardPreview } from "./hui-card-preview";
|
import { HuiCardPreview } from "./hui-card-preview";
|
||||||
import { LovelaceCardEditor } from "../types";
|
import { LovelaceCardEditor } from "../types";
|
||||||
import { YamlChangedEvent, ConfigValue, ConfigError } from "./types";
|
import {
|
||||||
|
YamlChangedEvent,
|
||||||
|
CardPickedEvent,
|
||||||
|
ConfigValue,
|
||||||
|
ConfigError,
|
||||||
|
} from "./types";
|
||||||
import { extYamlSchema } from "./yaml-ext-schema";
|
import { extYamlSchema } from "./yaml-ext-schema";
|
||||||
import { EntityConfig } from "../entity-rows/types";
|
import { EntityConfig } from "../entity-rows/types";
|
||||||
|
import { getCardElementTag } from "../common/get-card-element-tag";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@ -37,30 +53,17 @@ declare global {
|
|||||||
"config-changed": {
|
"config-changed": {
|
||||||
config: LovelaceCardConfig;
|
config: LovelaceCardConfig;
|
||||||
};
|
};
|
||||||
|
"cancel-edit-card": {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
|
||||||
|
|
||||||
export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
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 {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
_hass: {},
|
hass: {},
|
||||||
|
cardConfig: {},
|
||||||
|
viewId: {},
|
||||||
_cardId: {},
|
_cardId: {},
|
||||||
_originalConfig: {},
|
|
||||||
_configElement: {},
|
_configElement: {},
|
||||||
_configValue: {},
|
_configValue: {},
|
||||||
_configState: {},
|
_configState: {},
|
||||||
@ -68,34 +71,9 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
_uiEditor: {},
|
_uiEditor: {},
|
||||||
_saving: {},
|
_saving: {},
|
||||||
_loading: {},
|
_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 {
|
private get _dialog(): PaperDialogElement {
|
||||||
return this.shadowRoot!.querySelector("paper-dialog")!;
|
return this.shadowRoot!.querySelector("paper-dialog")!;
|
||||||
}
|
}
|
||||||
@ -104,9 +82,64 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
return this.shadowRoot!.querySelector("hui-card-preview")!;
|
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 {
|
protected render(): TemplateResult {
|
||||||
let content;
|
let content;
|
||||||
if (!this._configElement !== undefined) {
|
let preview;
|
||||||
|
if (this._configElement !== undefined) {
|
||||||
if (this._uiEditor) {
|
if (this._uiEditor) {
|
||||||
content = html`
|
content = html`
|
||||||
<div class="element-editor">${this._configElement}</div>
|
<div class="element-editor">${this._configElement}</div>
|
||||||
@ -121,6 +154,17 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
></hui-yaml-editor>
|
></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`
|
return html`
|
||||||
@ -142,16 +186,17 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
${content}
|
${content} ${preview}
|
||||||
<hr />
|
|
||||||
<hui-card-preview .hass="${this.hass}"></hui-card-preview>
|
|
||||||
</paper-dialog-scrollable>
|
</paper-dialog-scrollable>
|
||||||
${
|
${
|
||||||
!this._loading
|
!this._loading
|
||||||
? html`
|
? html`
|
||||||
<div class="paper-dialog-buttons">
|
<div class="paper-dialog-buttons">
|
||||||
<paper-button
|
<paper-button
|
||||||
?disabled="${!this._isToggleAvailable}"
|
?hidden="${!this._configValue || !this._configValue.value}"
|
||||||
|
?disabled="${
|
||||||
|
this._configElement === null || this._configState !== "OK"
|
||||||
|
}"
|
||||||
@click="${this._toggleEditor}"
|
@click="${this._toggleEditor}"
|
||||||
>${
|
>${
|
||||||
this.localize(
|
this.localize(
|
||||||
@ -163,7 +208,8 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
>${this.localize("ui.common.cancel")}</paper-button
|
>${this.localize("ui.common.cancel")}</paper-button
|
||||||
>
|
>
|
||||||
<paper-button
|
<paper-button
|
||||||
?disabled="${this._saving}"
|
?hidden="${!this._configValue || !this._configValue.value}"
|
||||||
|
?disabled="${this._saving || this._configState !== "OK"}"
|
||||||
@click="${this._save}"
|
@click="${this._save}"
|
||||||
>
|
>
|
||||||
<paper-spinner
|
<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 {
|
private _save(): void {
|
||||||
this._saving = true;
|
this._saving = true;
|
||||||
this._updateConfigInBackend();
|
this._updateConfigInBackend();
|
||||||
@ -283,7 +296,9 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _closeDialog(): void {
|
private _closeDialog(): void {
|
||||||
this._cardId = undefined;
|
this.cardConfig = undefined;
|
||||||
|
this.viewId = undefined;
|
||||||
|
fireEvent(this, "cancel-edit-card");
|
||||||
this._dialog.close();
|
this._dialog.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,21 +316,39 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (this.viewId) {
|
||||||
|
await addCard(
|
||||||
|
this.hass!,
|
||||||
|
String(this.viewId),
|
||||||
|
this._configValue!.value!,
|
||||||
|
this._configValue!.format
|
||||||
|
);
|
||||||
|
} else {
|
||||||
await updateCardConfig(
|
await updateCardConfig(
|
||||||
this.hass!,
|
this.hass!,
|
||||||
this._cardId!,
|
this._cardId!,
|
||||||
this._configValue!.value!,
|
this._configValue!.value!,
|
||||||
this._configValue!.format
|
this._configValue!.format
|
||||||
);
|
);
|
||||||
|
}
|
||||||
this._closeDialog();
|
this._closeDialog();
|
||||||
this._saveDone();
|
this._saveDone();
|
||||||
fireEvent(this, "reload-lovelace");
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Saving failed: ${err.message}`);
|
alert(`Saving failed: ${err.message}`);
|
||||||
this._saveDone();
|
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 {
|
private _handleYamlChanged(ev: YamlChangedEvent): void {
|
||||||
this._configValue = { format: "yaml", value: ev.detail.yaml };
|
this._configValue = { format: "yaml", value: ev.detail.yaml };
|
||||||
try {
|
try {
|
||||||
@ -324,11 +357,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
}) as LovelaceCardConfig;
|
}) as LovelaceCardConfig;
|
||||||
this._updatePreview(config);
|
this._updatePreview(config);
|
||||||
this._configState = "OK";
|
this._configState = "OK";
|
||||||
if (!this._isToggleAvailable && this._configElement !== null) {
|
|
||||||
this._isToggleAvailable = true;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._isToggleAvailable = false;
|
|
||||||
this._configState = "YAML_ERROR";
|
this._configState = "YAML_ERROR";
|
||||||
this._setPreviewError({
|
this._setPreviewError({
|
||||||
type: "YAML Error",
|
type: "YAML Error",
|
||||||
@ -365,8 +394,42 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
this._resizeDialog();
|
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() {
|
private _isConfigValid() {
|
||||||
if (!this._cardId || !this._configValue || !this._configValue.value) {
|
if (!this._configValue || !this._configValue.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this._configState === "OK") {
|
if (this._configState === "OK") {
|
||||||
@ -377,36 +440,36 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _isConfigChanged(): boolean {
|
private _isConfigChanged(): boolean {
|
||||||
|
if (this.viewId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const configValue =
|
const configValue =
|
||||||
this._configValue!.format === "yaml"
|
this._configValue!.format === "yaml"
|
||||||
? yaml.safeDump(this._configValue!.value)
|
? yaml.safeDump(this._configValue!.value)
|
||||||
: 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) {
|
if (!conf) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._errorMsg = undefined;
|
this._errorMsg = undefined;
|
||||||
this._loading = true;
|
this._loading = true;
|
||||||
this._configElement = undefined;
|
this._configElement = undefined;
|
||||||
this._isToggleAvailable = false;
|
|
||||||
|
|
||||||
const tag = conf.type.startsWith(CUSTOM_TYPE_PREFIX)
|
const tag = getCardElementTag(conf.type);
|
||||||
? conf!.type.substr(CUSTOM_TYPE_PREFIX.length)
|
|
||||||
: `hui-${conf!.type}-card`;
|
|
||||||
|
|
||||||
const elClass = customElements.get(tag);
|
const elClass = customElements.get(tag);
|
||||||
let configElement;
|
let configElement;
|
||||||
|
|
||||||
try {
|
if (elClass && elClass.getConfigElement) {
|
||||||
configElement = await elClass.getConfigElement();
|
configElement = await elClass.getConfigElement();
|
||||||
} catch (err) {
|
} else {
|
||||||
this._uiEditor = false;
|
this._uiEditor = false;
|
||||||
this._configElement = null;
|
this._configElement = null;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -418,7 +481,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
this._uiEditor = false;
|
this._uiEditor = false;
|
||||||
this._configElement = null;
|
this._configElement = null;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
configElement.hass = this.hass;
|
configElement.hass = this.hass;
|
||||||
@ -427,8 +490,9 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
);
|
);
|
||||||
this._configValue = { format: "json", value: conf };
|
this._configValue = { format: "json", value: conf };
|
||||||
this._configElement = configElement;
|
this._configElement = configElement;
|
||||||
this._isToggleAvailable = true;
|
await this.updateComplete;
|
||||||
this._updatePreview(conf);
|
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 {
|
export interface ConfigValue {
|
||||||
format: "json" | "yaml";
|
format: "json" | "yaml";
|
||||||
value?: string | LovelaceCardConfig;
|
value?: string | LovelaceCardConfig;
|
||||||
@ -30,3 +36,7 @@ export interface EditorTarget extends EventTarget {
|
|||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
configValue?: string;
|
configValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CardPickTarget extends EventTarget {
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
|
import "@polymer/paper-fab/paper-fab";
|
||||||
import "../../components/entity/ha-state-label-badge";
|
import "../../components/entity/ha-state-label-badge";
|
||||||
import "./components/hui-card-options";
|
import "./components/hui-card-options";
|
||||||
|
|
||||||
import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
|
import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
|
||||||
|
|
||||||
|
import EventsMixin from "../../mixins/events-mixin";
|
||||||
import createCardElement from "./common/create-card-element";
|
import createCardElement from "./common/create-card-element";
|
||||||
import { computeCardSize } from "./common/compute-card-size";
|
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() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
@ -44,6 +47,18 @@ class HUIView extends PolymerElement {
|
|||||||
margin: 4px 4px 8px;
|
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) {
|
@media (max-width: 500px) {
|
||||||
:host {
|
:host {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
@ -64,6 +79,13 @@ class HUIView extends PolymerElement {
|
|||||||
</style>
|
</style>
|
||||||
<div id="badges"></div>
|
<div id="badges"></div>
|
||||||
<div id="columns"></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 = [];
|
this._badges = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_addCard() {
|
||||||
|
showEditCardDialog(this, {
|
||||||
|
viewId: this.config.id,
|
||||||
|
add: true,
|
||||||
|
reloadLovelace: () => {
|
||||||
|
this.fire("config-refresh");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_createBadges(config) {
|
_createBadges(config) {
|
||||||
const root = this.$.badges;
|
const root = this.$.badges;
|
||||||
while (root.lastChild) {
|
while (root.lastChild) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user