From 7ca2ef4c4c9fa9d08b057cba8644bc1255e21df4 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Tue, 30 Oct 2018 06:15:02 -0400 Subject: [PATCH] Addition to Edit Love Lace Cards (#1885) * Initial Commit * Removing old code * Switching to litlement and ts * remove .ts from extension * Addressing a few reviews * Added ShowDialog Still no whammy * Fix some things * Extract one more data method * Add more types * Clean up imports * Call super * Finishing touches * Fix typescript check --- src/panels/lovelace/common/data.ts | 21 ++++ .../components/ha-dialog-edit-card.ts | 99 +++++++++++++++++++ .../lovelace/components/hui-card-options.ts | 68 +++++++++++++ src/panels/lovelace/hui-root.js | 76 +++++++++----- src/panels/lovelace/hui-view.js | 38 ++++--- tsconfig.json | 5 +- 6 files changed, 270 insertions(+), 37 deletions(-) create mode 100644 src/panels/lovelace/common/data.ts create mode 100644 src/panels/lovelace/components/ha-dialog-edit-card.ts create mode 100644 src/panels/lovelace/components/hui-card-options.ts diff --git a/src/panels/lovelace/common/data.ts b/src/panels/lovelace/common/data.ts new file mode 100644 index 0000000000..9baa221ab8 --- /dev/null +++ b/src/panels/lovelace/common/data.ts @@ -0,0 +1,21 @@ +import { HomeAssistant } from "../../../types"; + +export const getCardConfig = ( + hass: HomeAssistant, + cardId: string +): Promise => + hass.callWS({ + type: "lovelace/config/card/get", + card_id: cardId, + }); + +export const updateCardConfig = ( + hass: HomeAssistant, + cardId: string, + config: any +): Promise => + hass!.callWS({ + type: "lovelace/config/card/update", + card_id: cardId, + card_config: config, + }); diff --git a/src/panels/lovelace/components/ha-dialog-edit-card.ts b/src/panels/lovelace/components/ha-dialog-edit-card.ts new file mode 100644 index 0000000000..6e5a368c29 --- /dev/null +++ b/src/panels/lovelace/components/ha-dialog-edit-card.ts @@ -0,0 +1,99 @@ +import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; +import { fireEvent } from "../../../common/dom/fire_event.js"; + +import "@polymer/paper-button/paper-button.js"; +import "@polymer/paper-input/paper-textarea.js"; +import "@polymer/paper-dialog/paper-dialog.js"; +// This is not a duplicate import, one is for types, one is for element. +// tslint:disable-next-line +import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog.js"; +import { HomeAssistant } from "../../../types"; +import { getCardConfig, updateCardConfig } from "../common/data"; + +export class HuiEditCardModal extends LitElement { + protected hass?: HomeAssistant; + private _cardId?: string; + private _cardConfig?: string; + private _reloadLovelace?: () => void; + + static get properties(): PropertyDeclarations { + return { + hass: {}, + cardId: { + type: Number, + }, + _cardConfig: {}, + _dialogClosedCallback: {}, + }; + } + + public async showDialog({ hass, cardId, reloadLovelace }) { + this.hass = hass; + this._cardId = cardId; + this._reloadLovelace = reloadLovelace; + this._loadConfig(); + // Wait till dialog is rendered. + await this.updateComplete; + this._dialog.open(); + } + + private get _dialog(): PaperDialogElement { + return this.shadowRoot!.querySelector("paper-dialog")!; + } + + protected render() { + return html` + + +

Card Configuration

+ +
+ Cancel + Save +
+
+ `; + } + + private _closeDialog() { + this._dialog.close(); + } + + private async _loadConfig() { + this._cardConfig = await getCardConfig(this.hass!, this._cardId!); + await this.updateComplete; + // This will center the dialog with the updated config + fireEvent(this._dialog, "iron-resize"); + } + + private async _updateConfig() { + const newCardConfig = this.shadowRoot!.querySelector("paper-textarea")! + .value; + + if (this._cardConfig === newCardConfig) { + this._dialog.close(); + return; + } + try { + await updateCardConfig(this.hass!, this._cardId!, newCardConfig); + this._dialog.close(); + this._reloadLovelace!(); + } catch (err) { + alert(`Saving failed: ${err.reason}`); + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-dialog-edit-card": HuiEditCardModal; + } +} + +customElements.define("ha-dialog-edit-card", HuiEditCardModal); diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts new file mode 100644 index 0000000000..8bf2af28eb --- /dev/null +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -0,0 +1,68 @@ +import "@polymer/paper-button/paper-button.js"; +import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; +import { fireEvent } from "../../../common/dom/fire_event.js"; +import { HomeAssistant } from "../../../types.js"; + +let registeredDialog = false; + +export class HuiCardOptions extends LitElement { + public cardId?: string; + protected hass?: HomeAssistant; + + static get properties(): PropertyDeclarations { + return { + hass: {}, + }; + } + + public connectedCallback() { + super.connectedCallback(); + if (!registeredDialog) { + registeredDialog = true; + fireEvent(this, "register-dialog", { + dialogShowEvent: "show-edit-card", + dialogTag: "ha-dialog-edit-card", + dialogImport: () => import("./ha-dialog-edit-card"), + }); + } + } + + protected render() { + return html` + + +
+ EDIT +
+ `; + } + private _editCard() { + fireEvent(this, "show-edit-card", { + hass: this.hass, + cardId: this.cardId, + reloadLovelace: () => fireEvent(this, "config-refresh"), + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-card-options": HuiCardOptions; + } +} + +customElements.define("hui-card-options", HuiCardOptions); diff --git a/src/panels/lovelace/hui-root.js b/src/panels/lovelace/hui-root.js index 69e4fd45c9..716e1bbb17 100644 --- a/src/panels/lovelace/hui-root.js +++ b/src/panels/lovelace/hui-root.js @@ -84,28 +84,40 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) { > - - -
[[_computeTitle(config)]]
- - - - - - Refresh - Unused entities - Help - - -
+ +
@@ -170,6 +182,12 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) { computed: "_updateNotifications(hass.states, _persistentNotifications)", }, + _editMode: { + type: Boolean, + value: false, + observer: "_editModeChanged", + }, + routeData: Object, }; } @@ -255,6 +273,18 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) { window.open("https://www.home-assistant.io/lovelace/", "_blank"); } + _editModeEnable() { + this._editMode = true; + } + + _editModeDisable() { + this._editMode = false; + } + + _editModeChanged() { + this._selectView(this._curView); + } + _handleViewSelected(ev) { const index = ev.detail.selected; if (index !== this._curView) { @@ -284,10 +314,12 @@ class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) { if (viewConfig.panel) { view = createCardElement(viewConfig.cards[0]); view.isPanel = true; + view.editMode = this._editMode; } else { view = document.createElement("hui-view"); view.config = viewConfig; view.columns = this.columns; + view.editMode = this._editMode; } if (viewConfig.background) background = viewConfig.background; } diff --git a/src/panels/lovelace/hui-view.js b/src/panels/lovelace/hui-view.js index b13edb5a10..bc33439018 100644 --- a/src/panels/lovelace/hui-view.js +++ b/src/panels/lovelace/hui-view.js @@ -2,10 +2,12 @@ import { html } from "@polymer/polymer/lib/utils/html-tag.js"; import { PolymerElement } from "@polymer/polymer/polymer-element.js"; import "../../components/entity/ha-state-label-badge.js"; +import "./components/hui-card-options.ts"; import applyThemesOnElement from "../../common/dom/apply_themes_on_element.js"; import createCardElement from "./common/create-card-element"; +import computeCardSize from "./common/compute-card-size"; class HUIView extends PolymerElement { static get template() { @@ -73,6 +75,7 @@ class HUIView extends PolymerElement { }, config: Object, columns: Number, + editMode: Boolean, }; } @@ -80,7 +83,7 @@ class HUIView extends PolymerElement { return [ // Put all properties in 1 observer so we only call configChanged once "_createBadges(config)", - "_createCards(config, columns)", + "_createCards(config, columns, editMode)", ]; } @@ -130,11 +133,25 @@ class HUIView extends PolymerElement { return; } - const elements = config.cards.map((cardConfig) => { + const elements = []; + const elementsToAppend = []; + for (const cardConfig of config.cards) { const element = createCardElement(cardConfig); element.hass = this.hass; - return element; - }); + elements.push(element); + + if (!this.editMode) { + elementsToAppend.push(element); + continue; + } + + const wrapper = document.createElement("hui-card-options"); + wrapper.hass = this.hass; + wrapper.cardId = cardConfig.id; + wrapper.editMode = this.editMode; + wrapper.appendChild(element); + elementsToAppend.push(wrapper); + } let columns = []; const columnEntityCount = []; @@ -161,15 +178,10 @@ class HUIView extends PolymerElement { return minIndex; } - elements.forEach((el) => { - // Trigger custom elements to build up DOM. This is needed for some elements - // that use the DOM to decide their height. We don't have to clean this up - // because a DOM element can only be in 1 position, so it will be removed from - // 'this' and added to the correct column afterwards. - this.appendChild(el); - const cardSize = - typeof el.getCardSize === "function" ? el.getCardSize() : 1; - columns[getColumnIndex(cardSize)].push(el); + elements.forEach((el, index) => { + const cardSize = computeCardSize(el); + // Element to append might be the wrapped card when we're editing. + columns[getColumnIndex(cardSize)].push(elementsToAppend[index]); }); // Remove empty columns diff --git a/tsconfig.json b/tsconfig.json index b5402c3807..5911c25995 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es2017", - "module": "es2015", + "module": "esnext", "moduleResolution": "node", "lib": ["es2017", "dom", "dom.iterable"], "noEmit": true, @@ -10,6 +10,7 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "strict": true, - "noImplicitAny": false + "noImplicitAny": false, + "skipLibCheck": true } }