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:
Bram Kragten 2018-12-03 14:11:46 +01:00 committed by Paulus Schoutsen
parent 57b5db4f43
commit f461ad6d31
13 changed files with 409 additions and 144 deletions

9
src/common/util/uid.ts Normal file
View 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();
}

View File

@ -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,
});

View File

@ -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[];

View File

@ -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;

View 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`;
}

View File

@ -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"),
});
}

View File

@ -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);

View 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);

View File

@ -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);

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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) {