diff --git a/compact-custom-header.js b/compact-custom-header.js index 29df189..066ec62 100644 --- a/compact-custom-header.js +++ b/compact-custom-header.js @@ -50,6 +50,7 @@ class CompactCustomHeader { this.defaultConfig = { header: true, disable: false, + yaml_editor: true, menu: "show", voice: "show", notifications: "show", @@ -116,10 +117,7 @@ class CompactCustomHeader { this.hideMenuItems(); this.styleHeader(tabContainer, tabs); this.styleButtons(tabs, tabContainer); - if (this.firstRun) { - this.sidebarMod(); - this.conditionalStyling(tabs, this.header); - } + if (this.firstRun) this.sidebarMod(); this.hideTabs(tabContainer, tabs); for (let button in this.buttons) { if (this.cchConfig[button] == "clock") this.insertClock(button); @@ -262,7 +260,7 @@ class CompactCustomHeader { } else if (target.id == "view" && addedNodes.length) { // Navigating to new tab/view. this.run(); - this.scrollTabIconIntoView(); + if (tabContainer) this.scrollTabIconIntoView(); } }); }; @@ -394,7 +392,10 @@ class CompactCustomHeader { } insertEditMenu(tabs, disabled) { - if (this.buttons.options && this.editMode) { + if ( + this.buttons.options && + (this.editMode || this.lovelace.mode == "yaml" && this.cchConfig.yaml_editor) + ) { // If any tabs are hidden, add "show all tabs" option. if (this.cchConfig.hide_tabs && !this.cchConfig.edit_mode_show_tabs) { let show_tabs = document.createElement("paper-item"); @@ -527,6 +528,7 @@ class CompactCustomHeader { this.view.style.paddingTop = "48.5px"; this.view.style.boxSizing = "border-box"; this.header.style.background = this.prevColor.background; + this.conditionalStyling(tabs, this.header); this.header.querySelector("app-toolbar").style.background = "transparent"; if ( this.frontendVersion >= 20190911 && @@ -536,13 +538,13 @@ class CompactCustomHeader { viewStyle.setAttribute("id", "cch_view_styling"); viewStyle.innerHTML = ` hui-view { - margin-top: -48.5px; + margin-top: -52px; padding-top: 52px; min-height: 100vh; ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} } hui-panel-view { - margin-top: -48.5px; + margin-top: -52px; padding-top: 52px; min-height: calc(100vh - 52px); ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} @@ -1347,7 +1349,10 @@ class CompactCustomHeader { ? buttonElem.querySelector("paper-icon-button") : buttonElem.shadowRoot.querySelector("paper-icon-button"); if (styleTarget == "icon") { - iconTarget.setAttribute("icon", this.templateEval(tempCond, states)); + iconTarget.setAttribute( + "icon", + this.templateEval(tempCond, states) + ); } else if (styleTarget == "color") { let tar = iconTarget.querySelector("iron-icon") || @@ -1475,6 +1480,7 @@ class CompactCustomHeader { swipeNavigation(tabs, tabContainer) { // To make it easier to update lovelace-swipe-navigation // keep this as close to the standalone lovelace addon as possible. + if (!tabContainer) return; let swipe_amount = this.cchConfig.swipe_amount || 15; let swipe_groups = this.cchConfig.swipe_groups; let animate = this.cchConfig.swipe_animate || "none"; @@ -1949,23 +1955,32 @@ class CompactCustomHeaderEditor extends cch.LitElement { } } let newConfig = { ...this._lovelace.config, ...{ cch: this._config } }; - try { - this._lovelace.saveConfig(newConfig).then(() => { - location.reload(true); - }); - } catch (e) { - alert(`Save failed: ${e}`); + if (cch.lovelace.mode == "storage") { + try { + this._lovelace.saveConfig(newConfig).then(() => { + window.location.href = window.location.href; + }); + } catch (e) { + alert(`Save failed: ${e}`); + } + } else { + window.prompt( + "Copy to clipboard: Ctrl+C, Enter\n" + + "This option is experimental, check the copied config and backup.", + this.obj2yaml({ cch: newConfig.cch }) + ); } } get _save_button() { + let text = cch.lovelace.mode == "storage" ? "Save and Reload" : "Copy YAML"; return this._mwc_button ? this.html` - Save and Reload + ${text} ` : this.html` Save and Reload${text} `; } @@ -2032,6 +2047,105 @@ class CompactCustomHeaderEditor extends cch.LitElement { return result; } + obj2yaml(obj) { + if (typeof obj == "string") obj = JSON.parse(obj); + const ret = []; + convert(obj, ret); + return ret.join("\n"); + function getType(obj) { + if (obj instanceof Array) { + return "array"; + } else if (typeof obj == "string") { + return "string"; + } else if (typeof obj == "boolean") { + return "boolean"; + } else if (typeof obj == "number") { + return "number"; + } else if (typeof obj == "undefined" || obj === null) { + return "null"; + } else { + return "hash"; + } + } + function convert(obj, ret) { + const type = getType(obj); + switch (getType(obj)) { + case "array": + convertArray(obj, ret); + break; + case "hash": + convertHash(obj, ret); + break; + case "string": + convertString(obj, ret); + break; + case "null": + ret.push("null"); + break; + case "number": + ret.push(obj.toString()); + break; + case "boolean": + ret.push(obj ? "true" : "false"); + break; + } + } + function convertArray(obj, ret) { + if (obj.length === 0) ret.push("[]"); + for (let i = 0; i < obj.length; i++) { + const ele = obj[i]; + const recurse = []; + convert(ele, recurse); + for (let j = 0; j < recurse.length; j++) { + ret.push((j == 0 ? "- " : " ") + recurse[j]); + } + } + } + function convertHash(obj, ret) { + for (const k in obj) { + const recurse = []; + if (obj.hasOwnProperty(k)) { + const ele = obj[k]; + convert(ele, recurse); + const type = getType(ele); + if ( + type == "string" || + type == "null" || + type == "number" || + type == "boolean" + ) { + ret.push(`${k}: ${recurse[0]}`); + } else { + ret.push(`${k}: `); + for (let i = 0; i < recurse.length; i++) { + ret.push(` ${recurse[i]}`); + } + } + } + } + } + function convertString(obj, ret) { + if ((obj.includes("'") && obj.includes('"')) || obj.length > 45) { + if (obj.includes(";")) { + obj = obj.includes("; ") ? obj.split("; ") : obj.split(";"); + obj[0] = `>\n ${obj[0]}`; + if (obj[obj.length - 1].trim() == "") obj.pop(); + obj = obj.join(";\n "); + obj = obj.replace(/\n$/, ""); + ret.push(obj); + } else { + ret.push(`>\n ${obj}`); + } + } else if (obj.includes('"')) { + obj = obj.replace(/\n$/, ""); + ret.push(`'${obj}'`); + } else { + obj = obj.replace(/\n$/, ""); + ret.push(`"${obj}"`); + } + } + } + renderStyle() { return this.html`