From a5fb833ab19fdf80aa728e9c31dc3e1969d0edcf Mon Sep 17 00:00:00 2001 From: dfcarvajal Date: Mon, 9 Dec 2019 12:40:02 +0000 Subject: [PATCH] =?UTF-8?q?Actualizaci=C3=B3n=20m=C3=B3dulos=20de=20hacs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compact-custom-header.js | 3870 ++++++++++------- .../compact-custom-header.js.gz | Bin 15213 -> 19672 bytes .../entity-attributes-card.js.gz | Bin 1277 -> 1277 bytes .../lovelace-auto-entities/auto-entities.js | 227 +- .../auto-entities.js.gz | Bin 1949 -> 2888 bytes .../lovelace-auto-entities/webpack.config.js | 10 + .../webpack.config.js.gz | Bin 0 -> 175 bytes .../lovelace-card-tools/card-tools.js | 429 +- .../lovelace-card-tools/card-tools.js.gz | Bin 4107 -> 4477 bytes .../lovelace-card-tools/webpack.config.js | 10 + .../lovelace-card-tools/webpack.config.js.gz | Bin 0 -> 173 bytes .../fold-entity-row.js | 20 +- .../fold-entity-row.js.gz | Bin 2415 -> 2524 bytes .../webpack.config.js | 10 + .../webpack.config.js.gz | Bin 0 -> 177 bytes .../lovelace-layout-card/layout-card.js | 200 +- .../lovelace-layout-card/layout-card.js.gz | Bin 1715 -> 3350 bytes .../lovelace-layout-card/webpack.config.js | 10 + .../lovelace-layout-card/webpack.config.js.gz | Bin 0 -> 173 bytes .../multiple-entity-row.js | 138 +- .../multiple-entity-row.js.gz | Bin 1900 -> 2077 bytes .../slider-entity-row.js | 16 +- .../slider-entity-row.js.gz | Bin 2068 -> 2794 bytes .../webpack.config.js | 10 + .../webpack.config.js.gz | Bin 0 -> 178 bytes .../xiaomi-vacuum-card.js | 67 +- .../xiaomi-vacuum-card.js.gz | Bin 2004 -> 2342 bytes .../mini-media-player-bundle.js | 1 + .../mini-media-player-bundle.js.gz | Bin 0 -> 21527 bytes .../mini-media-player/mini-media-player.js | 2 +- .../mini-media-player/mini-media-player.js.gz | Bin 20613 -> 20872 bytes www/community/monster-card/monster-card.js | 122 - www/community/monster-card/monster-card.js.gz | Bin 1310 -> 0 bytes .../secondaryinfo-entity-row.js.gz | Bin 779 -> 779 bytes .../vertical-stack-in-card.js | 44 +- .../vertical-stack-in-card.js.gz | Bin 1826 -> 1582 bytes 36 files changed, 2532 insertions(+), 2654 deletions(-) create mode 100644 www/community/lovelace-auto-entities/webpack.config.js create mode 100644 www/community/lovelace-auto-entities/webpack.config.js.gz create mode 100644 www/community/lovelace-card-tools/webpack.config.js create mode 100644 www/community/lovelace-card-tools/webpack.config.js.gz create mode 100644 www/community/lovelace-fold-entity-row/webpack.config.js create mode 100644 www/community/lovelace-fold-entity-row/webpack.config.js.gz create mode 100644 www/community/lovelace-layout-card/webpack.config.js create mode 100644 www/community/lovelace-layout-card/webpack.config.js.gz create mode 100644 www/community/lovelace-slider-entity-row/webpack.config.js create mode 100644 www/community/lovelace-slider-entity-row/webpack.config.js.gz create mode 100644 www/community/mini-media-player/mini-media-player-bundle.js create mode 100644 www/community/mini-media-player/mini-media-player-bundle.js.gz delete mode 100644 www/community/monster-card/monster-card.js delete mode 100644 www/community/monster-card/monster-card.js.gz diff --git a/www/community/compact-custom-header/compact-custom-header.js b/www/community/compact-custom-header/compact-custom-header.js index 892e07c..3734973 100644 --- a/www/community/compact-custom-header/compact-custom-header.js +++ b/www/community/compact-custom-header/compact-custom-header.js @@ -1,998 +1,1477 @@ -const LitElement = Object.getPrototypeOf( - customElements.get("ha-panel-lovelace") +console.info( + `%c COMPACT-CUSTOM-HEADER \n%c Version 1.4.9 `, + "color: orange; font-weight: bold; background: black", + "color: white; font-weight: bold; background: dimgray" ); -const html = LitElement.prototype.html; -const hass = document.querySelector("home-assistant").hass; -const fireEvent = (node, type, detail, options) => { - options = options || {}; - detail = detail === null || detail === undefined ? {} : detail; - const event = new Event(type, { - bubbles: options.bubbles === undefined ? true : options.bubbles, - cancelable: Boolean(options.cancelable), - composed: options.composed === undefined ? true : options.composed - }); - event.detail = detail; - node.dispatchEvent(event); - return event; -}; +class CompactCustomHeader { + constructor() { + this.LitElement = Object.getPrototypeOf( + customElements.get("ha-panel-lovelace") + ); + this.hass = document.querySelector("home-assistant").hass; + this.fireEvent = (node, type, detail, options = {}) => { + detail = detail === null || detail === undefined ? {} : detail; + const event = new Event(type, { + bubbles: options.bubbles === undefined ? true : options.bubbles, + cancelable: Boolean(options.cancelable), + composed: options.composed === undefined ? true : options.composed + }); + event.detail = detail; + node.dispatchEvent(event); + return event; + }; -const defaultConfig = { - header: true, - disable: false, - menu: "show", - voice: "show", - notifications: "show", - options: "show", - clock_format: 12, - clock_am_pm: true, - clock_date: false, - date_locale: hass.language, - chevrons: false, - redirect: true, - background: "", - hide_tabs: [], - show_tabs: [], - default_tab: [], - kiosk_mode: false, - sidebar_swipe: true, - sidebar_closed: false, - disable_sidebar: false, - hide_help: false, - hide_config: false, - hide_unused: false, - tab_color: {}, - button_color: {}, - swipe: false, - swipe_amount: "15", - swipe_animate: "none", - swipe_skip: "", - swipe_wrap: true, - swipe_prevent_default: false, - warning: true, - compact_header: true -}; + let ll = document.querySelector("home-assistant"); + ll = ll && ll.shadowRoot; + ll = ll && ll.querySelector("home-assistant-main"); + this.main = ll; + ll = ll && ll.shadowRoot; + ll = ll && ll.querySelector("app-drawer-layout partial-panel-resolver"); + this.panelResolver = ll; + ll = (ll && ll.shadowRoot) || ll; + ll = ll && ll.querySelector("ha-panel-lovelace"); + ll = ll && ll.shadowRoot; + ll = ll && ll.querySelector("hui-root"); + this.lovelace = ll.lovelace; + this.root = ll.shadowRoot; -let root = document.querySelector("home-assistant"); -root = root && root.shadowRoot; -root = root && root.querySelector("home-assistant-main"); -const main = root; -root = root && root.shadowRoot; -root = root && root.querySelector("app-drawer-layout partial-panel-resolver"); -root = (root && root.shadowRoot) || root; -root = root && root.querySelector("ha-panel-lovelace"); -root = root && root.shadowRoot; -root = root && root.querySelector("hui-root"); -const lovelace = root.lovelace; -root = root.shadowRoot; + this.frontendVersion = Number(window.frontendVersion); + this.newSidebar = this.frontendVersion >= 20190710; + this.header = this.root.querySelector("app-header"); + this.editMode = this.header.className == "edit-mode"; + this.view = this.root.querySelector("ha-app-layout #view"); -const newSidebar = !root.querySelector("hui-notification-drawer"); + this.sidebarClosed = false; + this.firstRun = true; + this.buttons = {}; + this.prevColor = {}; -let notifications = notificationCount(); -const header = root.querySelector("app-header"); -let cchConfig = buildConfig(lovelace.config.cch || {}); -const view = root.querySelector("ha-app-layout").querySelector('[id="view"]'); + this.defaultConfig = { + header: true, + disable: false, + yaml_editor: false, + menu: "show", + voice: "show", + notifications: "show", + options: "show", + clock_format: 12, + clock_am_pm: true, + clock_date: false, + date_locale: this.hass.language, + chevrons: false, + redirect: true, + background: "", + hide_tabs: "", + show_tabs: "", + edit_mode_show_tabs: false, + default_tab: "", + default_tab_template: "", + kiosk_mode: false, + sidebar_swipe: true, + sidebar_closed: false, + disable_sidebar: false, + hide_help: false, + hide_config: false, + hide_unused: false, + tab_color: {}, + button_color: {}, + statusbar_color: "", + swipe: false, + swipe_amount: "15", + swipe_animate: "none", + swipe_skip: "", + swipe_wrap: true, + swipe_prevent_default: false, + swipe_skip_hidden: true, + warning: true, + compact_header: true, + view_css: "", + time_css: "", + date_css: "", + header_css: "", + tab_css: {}, + button_css: {} + }; -let defaultTabRedirect = false; -let sidebarClosed = false; -let editMode = header.className == "edit-mode"; -let firstRun = true; -let condState = []; -let prevColor = {}; -let prevState = []; -let buttons = {}; - -run(); -breakingChangeNotification(); - -function run() { - const disable = cchConfig.disable; - const urlDisable = window.location.href.includes("disable_cch"); - const tabContainer = root.querySelector("paper-tabs"); - const tabs = tabContainer - ? Array.from(tabContainer.querySelectorAll("paper-tab")) - : []; - - if (firstRun || buttons == undefined) { - buttons = getButtonElements(tabContainer); - } - if (!buttons.menu || !buttons.options || header.className == "edit-mode") { - return; + this.cchConfig = this.buildConfig( + this.lovelace.config.cch || {}, + this.hass.user.name + ); } - if (!disable && !urlDisable) { - insertEditMenu(tabs); - hideMenuItems(); - styleHeader(tabContainer, tabs, header); - styleButtons(tabs); - defaultTab(tabs, tabContainer); - hideTabs(tabContainer, tabs); - for (let button in buttons) { - if (cchConfig[button] == "clock") insertClock(button); + run() { + const tabContainer = this.root.querySelector("paper-tabs"); + const tabs = tabContainer + ? Array.from(tabContainer.querySelectorAll("paper-tab")) + : []; + let disabled = + window.location.href.includes("disable_cch") || this.cchConfig.disable; + + if (this.firstRun || this.buttons == undefined) { + this.buttons = this.getButtonElements(tabContainer); } - if (firstRun) { - sidebarMod(); - conditionalStyling(tabs, header); - } - if (!editMode) tabContainerMargin(tabContainer); - if (cchConfig.swipe) swipeNavigation(tabs, tabContainer); - } - - if (!disable && firstRun) observers(tabContainer, tabs, urlDisable, header); - fireEvent(header, "iron-resize"); - firstRun = false; -} - -function buildConfig(config) { - let exceptionConfig = {}; - let highestMatch = 0; - if (config.exceptions) { - config.exceptions.forEach(exception => { - const matches = countMatches(exception.conditions); - if (matches > highestMatch) { - highestMatch = matches; - exceptionConfig = exception.config; - } - }); - } - - if ( - exceptionConfig.hide_tabs && - config.show_tabs && - exceptionConfig.hide_tabs.length && - config.show_tabs.length - ) { - delete config.show_tabs; - } else if ( - exceptionConfig.show_tabs && - config.hide_tabs && - exceptionConfig.show_tabs.length && - config.hide_tabs.length - ) { - delete config.hide_tabs; - } - return { ...defaultConfig, ...config, ...exceptionConfig }; - - function countMatches(conditions) { - const userVars = { user: hass.user.name, user_agent: navigator.userAgent }; - let count = 0; - for (const cond in conditions) { - if (cond == "user" && conditions[cond].includes(",")) { - let userList = conditions[cond].split(/[ ,]+/); - for (let user in userList) { - if (userVars[cond] == userList[user]) { - count++; - } - } - } else { - if ( - userVars[cond] == conditions[cond] || - (cond == "query_string" && - window.location.search.includes(conditions[cond])) || - (cond == "user_agent" && userVars[cond].includes(conditions[cond])) || - (cond == "media_query" && window.matchMedia(conditions[cond]).matches) - ) { - count++; - } else { - return 0; - } + if (!this.buttons.menu || !this.buttons.options || this.editMode) return; + if (!disabled) { + this.insertEditMenu(tabs); + this.hideMenuItems(); + this.styleHeader(tabContainer, tabs); + this.styleButtons(tabs, tabContainer); + if (this.firstRun) this.sidebarMod(); + this.hideTabs(tabContainer, tabs); + for (let button in this.buttons) { + if (this.cchConfig[button] == "clock") this.insertClock(button); } + if (!this.editMode) this.tabContainerMargin(tabContainer); + if (this.cchConfig.swipe) this.swipeNavigation(tabs, tabContainer); + if (this.firstRun) this.defaultTab(tabs, tabContainer); } - return count; - } -} - -function observers(tabContainer, tabs, urlDisable, header) { - const callback = function(mutations) { - mutations.forEach(mutation => { - if (mutation.target.className == "edit-mode") { - editMode = true; - if (!cchConfig.disable) removeStyles(tabContainer, tabs, header); - buttons.options = root.querySelector("paper-menu-button"); - insertEditMenu(tabs); - } else if (mutation.target.nodeName == "APP-HEADER") { - for (let node of mutation.addedNodes) { - if (node.nodeName == "APP-TOOLBAR") { - editMode = false; - buttons = getButtonElements(tabContainer); - run(); - return; - } - } - } else if (mutation.addedNodes.length) { - if (mutation.addedNodes[0].nodeName == "HUI-UNUSED-ENTITIES") return; - let editor = root - .querySelector("ha-app-layout") - .querySelector("editor"); - if (editor) root.querySelector("ha-app-layout").removeChild(editor); - if (!editMode && !urlDisable && cchConfig.conditional_styles) { - conditionalStyling(tabs, header); - } - } - }); - }; - new MutationObserver(callback).observe(view, { childList: true }); - new MutationObserver(callback).observe(root.querySelector("app-header"), { - childList: true - }); - - if (!urlDisable) { - window.hassConnection.then(({ conn }) => { - conn.socket.onmessage = () => { - notifications = notificationCount(); - if (cchConfig.conditional_styles && !editMode) { - conditionalStyling(tabs, header); - } - }; - }); - } -} - -function notificationCount() { - if (newSidebar) { - let badge = main.shadowRoot - .querySelector("ha-sidebar") - .shadowRoot.querySelector("span.notification-badge"); - if (!badge) { - return 0; - } else { - return parseInt(badge.innerHTML); + if (this.firstRun) { + this.observers(tabContainer, tabs, disabled); + this.breakingChangeNotification(); } + this.firstRun = false; + this.fireEvent(this.header, "iron-resize"); } - let i = 0; - let drawer = root - .querySelector("hui-notification-drawer") - .shadowRoot.querySelector(".notifications"); - for (let notification of drawer.querySelectorAll(".notification")) { - if (notification.style.display !== "none") i++; - } - return i; -} -function getButtonElements(tabContainer) { - let buttons = {}; - buttons.options = root.querySelector("paper-menu-button"); - if (!editMode) { - buttons.menu = root.querySelector("ha-menu-button"); - buttons.voice = root.querySelector("ha-start-voice-button"); - if (!newSidebar) { - buttons.notifications = root.querySelector("hui-notifications-button"); - } - } - if (buttons.menu && newSidebar) { - new MutationObserver(() => { - if (buttons.menu.style.visibility == "hidden") { - buttons.menu.style.display = "none"; - } else { - buttons.menu.style.display = ""; - } - tabContainerMargin(tabContainer); - }).observe(buttons.menu, { attributeFilter: ["style"] }); - } - return buttons; -} - -function tabContainerMargin(tabContainer) { - let marginRight = 0; - let marginLeft = 15; - for (const button in buttons) { - let visible = buttons[button].style.display !== "none"; - if (cchConfig[button] == "show" && visible) { - if (button == "menu") marginLeft += 45; - else marginRight += 45; - } else if (cchConfig[button] == "clock" && visible) { - const clockWidth = - (cchConfig.clock_format == 12 && cchConfig.clock_am_pm) || - cchConfig.clock_date - ? 110 - : 80; - if (button == "menu") marginLeft += clockWidth + 15; - else marginRight += clockWidth; - } - } - if (tabContainer) { - tabContainer.style.marginRight = marginRight + "px"; - tabContainer.style.marginLeft = marginLeft + "px"; - } -} - -function hideMenuItems() { - if (cchConfig.hide_help || cchConfig.hide_config || cchConfig.hide_unused) { - let menuItems = buttons.options - .querySelector("paper-listbox") - .querySelectorAll("paper-item"); - for (let item of menuItems) { - if ( - (item.innerHTML.includes("Help") || - item.getAttribute("aria-label") == "Help") && - cchConfig.hide_help - ) { - item.parentNode.removeChild(item); - } else if ( - (item.innerHTML.includes("Unused entities") || - item.getAttribute("aria-label") == "Unused entities") && - cchConfig.hide_unused - ) { - item.parentNode.removeChild(item); - } else if ( - (item.innerHTML.includes("Configure UI") || - item.getAttribute("aria-label") == "Configure UI") && - cchConfig.hide_config - ) { - item.parentNode.removeChild(item); - } - } - } -} - -function insertEditMenu(tabs) { - if (buttons.options && editMode) { - if (cchConfig.hide_tabs) { - let show_tabs = document.createElement("paper-item"); - show_tabs.setAttribute("id", "show_tabs"); - show_tabs.addEventListener("click", () => { - for (let i = 0; i < tabs.length; i++) { - tabs[i].style.removeProperty("display"); + buildConfig(config, user_name) { + let exceptionConfig = {}; + let highestMatch = 0; + // Count number of matching conditions and choose config with most matches. + if (config.exceptions) { + config.exceptions.forEach(exception => { + const matches = countMatches(exception.conditions, user_name); + if (matches > highestMatch) { + highestMatch = matches; + exceptionConfig = exception.config; } }); - show_tabs.innerHTML = "Show all tabs"; - insertMenuItem(buttons.options.querySelector("paper-listbox"), show_tabs); + } + // If exception config uses hide_tabs and main config uses show_tabs, + // delete show_tabs and vice versa. + if ( + exceptionConfig.hide_tabs && + config.show_tabs && + exceptionConfig.hide_tabs.length && + config.show_tabs.length + ) { + delete config.show_tabs; + } else if ( + exceptionConfig.show_tabs && + config.hide_tabs && + exceptionConfig.show_tabs.length && + config.hide_tabs.length + ) { + delete config.hide_tabs; } - let cchSettings = document.createElement("paper-item"); - cchSettings.setAttribute("id", "cch_settings"); - cchSettings.addEventListener("click", () => { - showEditor(); - }); - cchSettings.innerHTML = "CCH Settings"; - insertMenuItem(buttons.options.querySelector("paper-listbox"), cchSettings); - } -} + return { ...this.defaultConfig, ...config, ...exceptionConfig }; -function removeStyles(tabContainer, tabs, header) { - let header_colors = root.querySelector('[id="cch_header_colors"]'); - if (tabContainer) { - tabContainer.style.marginLeft = ""; - tabContainer.style.marginRight = ""; - } - header.style.background = null; - view.style.minHeight = ""; - view.style.marginTop = ""; - view.style.paddingTop = ""; - view.style.boxSizing = ""; - if (root.querySelector('[id="cch_iron_selected"]')) { - root.querySelector('[id="cch_iron_selected"]').outerHTML = ""; - } - if (header_colors) header_colors.parentNode.removeChild(header_colors); - for (let i = 0; i < tabs.length; i++) { - tabs[i].style.color = ""; - } -} - -function styleHeader(tabContainer, tabs, header) { - if (!cchConfig.header || cchConfig.kiosk_mode) { - header.style.display = "none"; - view.style.minHeight = "100vh"; - } else { - view.style.minHeight = "100vh"; - view.style.marginTop = "-48.5px"; - view.style.paddingTop = "48.5px"; - view.style.boxSizing = "border-box"; - header.style.background = - cchConfig.background || - getComputedStyle(document.body).getPropertyValue("--cch-background") || - "var(--primary-color)"; - header.querySelector("app-toolbar").style.background = "transparent"; + function countMatches(conditions, user_name) { + const userVars = { + user: user_name, + user_agent: navigator.userAgent + }; + let count = 0; + for (const cond in conditions) { + if (cond == "user" && conditions[cond].includes(",")) { + conditions[cond].split(/[ ,]+/).forEach(user => { + if (userVars[cond] == user) count++; + }); + } else { + if ( + userVars[cond] == conditions[cond] || + (cond == "query_string" && + window.location.search.includes(conditions[cond])) || + (cond == "user_agent" && + userVars[cond].includes(conditions[cond])) || + (cond == "media_query" && + window.matchMedia(conditions[cond]).matches) + ) { + count++; + } else { + return 0; + } + } + } + return count; + } } - if (newSidebar && cchConfig.compact_header) { - let sidebar = main.shadowRoot.querySelector("ha-sidebar").shadowRoot; - sidebar.querySelector(".menu").style = "height:49px;"; - sidebar.querySelector("paper-listbox").style = "height:calc(100% - 180px);"; - } - let indicator = cchConfig.tab_indicator_color; - if ( - indicator && - !root.querySelector('[id="cch_header_colors"]') && - !editMode - ) { - let style = document.createElement("style"); - style.setAttribute("id", "cch_header_colors"); - style.innerHTML = ` - paper-tabs { - ${ - indicator - ? `--paper-tabs-selection-bar-color: ${indicator} !important` - : "var(--cch-tab-indicator-color) !important" + observers(tabContainer, tabs, disabled) { + // Watch for changes in Lovelace. + const callback = mutations => { + // Theme changed. + if (mutations[0].target.nodeName == "HTML") { + mutations = [mutations[0]]; + this.styleHeader(tabContainer, tabs); + this.conditionalStyling(tabs); + return; + } + mutations.forEach(({ addedNodes, target }) => { + if (addedNodes.length && target.nodeName == "PARTIAL-PANEL-RESOLVER") { + // Navigated back to lovelace from elsewhere in HA. + this.buttons = this.getButtonElements(); + this.run(); + } else if (target.className == "edit-mode" && addedNodes.length) { + // Entered edit mode. + this.editMode = true; + if (!disabled) this.removeStyles(tabContainer, tabs, this.header); + this.buttons.options = this.root.querySelector("paper-menu-button"); + this.insertEditMenu(tabs); + this.fireEvent(this.header, "iron-resize"); + } else if (target.nodeName == "APP-HEADER" && addedNodes.length) { + // Exited edit mode. + let editor = this.root + .querySelector("ha-app-layout") + .querySelector("editor"); + if (editor) { + this.root.querySelector("ha-app-layout").removeChild(editor); + } + for (let node of addedNodes) { + if (node.nodeName == "APP-TOOLBAR") { + this.editMode = false; + this.buttons = this.getButtonElements(); + this.root.querySelectorAll("[id^='cch']").forEach(style => { + style.remove(); + }); + setTimeout(() => { + this.run(); + if (!disabled) this.conditionalStyling(tabs, this.header); + }, 100); } } - `; - root.appendChild(style); + } else if ( + // Viewing unused entities + this.frontendVersion < 20190911 && + addedNodes.length && + !addedNodes[0].nodeName == "HUI-UNUSED-ENTITIES" + ) { + let editor = this.root + .querySelector("ha-app-layout") + .querySelector("editor"); + if (editor) { + this.root.querySelector("ha-app-layout").removeChild(editor); + } + if (this.cchConfig.conditional_styles) { + this.buttons = this.getButtonElements(tabContainer); + this.conditionalStyling(tabs, this.header); + } + } else if (target.id == "view" && addedNodes.length) { + // Navigating to new tab/view. + this.run(); + if (tabContainer) this.scrollTabIconIntoView(); + } + }); + }; + let observer = new MutationObserver(callback); + observer.observe(this.panelResolver, { childList: true }); + observer.observe(document.querySelector("html"), { attributes: true }); + observer.observe(this.view, { childList: true }); + observer.observe(this.root.querySelector("app-header"), { + childList: true + }); + + if (!disabled) { + // Watch for changes in entities. + window.hassConnection.then(({ conn }) => { + conn.socket.onmessage = () => { + if (this.cchConfig.conditional_styles && !this.editMode) { + this.conditionalStyling(tabs, this.header); + } + }; + }); + } } - let conditionalTabs = cchConfig.conditional_styles - ? JSON.stringify(cchConfig.conditional_styles).includes("tab") - : false; - if ( - !root.querySelector('[id="cch_iron_selected"]') && - !editMode && - !conditionalTabs && - tabContainer - ) { - let style = document.createElement("style"); - style.setAttribute("id", "cch_iron_selected"); - style.innerHTML = ` - .iron-selected { + getButtonElements(disabled) { + let buttons = {}; + buttons.options = this.root.querySelector("paper-menu-button"); + if (!this.editMode) { + buttons.menu = this.root.querySelector("ha-menu-button"); + buttons.voice = + this.root.querySelector("ha-start-voice-button") || + this.root.querySelector('[icon="hass:microphone"]'); + if (!this.newSidebar) { + buttons.notifications = this.root.querySelector( + "hui-notifications-button" + ); + } + } + // Remove space taken up by "hidden" menu button anytime we get buttons. + if ( + buttons.menu && + buttons.menu.style.visibility == "hidden" && + !disabled + ) { + buttons.menu.style.display = "none"; + } else if (buttons.menu) { + buttons.menu.style.display = ""; + } + return buttons; + } + + tabContainerMargin(tabContainer) { + let marginRight = 0; + let marginLeft = 15; + for (const button in this.buttons) { + if (!this.buttons[button]) continue; + let paperIconButton = + this.buttons[button].querySelector("paper-icon-button") || + this.buttons[button].shadowRoot.querySelector("paper-icon-button"); + let visible = paperIconButton + ? this.buttons[button].style.display !== "none" && + !paperIconButton.hasAttribute("hidden") + : this.buttons[button].style.display !== "none"; + if (this.cchConfig[button] == "show" && visible) { + if (button == "menu") marginLeft += 45; + else marginRight += 45; + } else if (this.cchConfig[button] == "clock" && visible) { + const clockWidth = + (this.cchConfig.clock_format == 12 && this.cchConfig.clock_am_pm) || + this.cchConfig.clock_date + ? 110 + : 80; + if (button == "menu") marginLeft += clockWidth + 15; + else marginRight += clockWidth; + } + } + if (tabContainer) { + tabContainer.style.marginRight = `${marginRight}px`; + tabContainer.style.marginLeft = `${marginLeft}px`; + } + } + + scrollTabIconIntoView() { + let paperTabs = this.root.querySelector("paper-tabs"); + let currentTab = paperTabs.querySelector(".iron-selected"); + if (!paperTabs || !currentTab) return; + let tab = currentTab.getBoundingClientRect(); + let container = paperTabs.shadowRoot + .querySelector("#tabsContainer") + .getBoundingClientRect(); + // If tab's icon isn't in view scroll it in. + if (container.right < tab.right || container.left > tab.left) { + if ("scrollMarginInline" in document.documentElement.style) { + currentTab.scrollIntoView({ inline: "center" }); + } else if (Element.prototype.scrollIntoViewIfNeeded) { + currentTab.scrollIntoViewIfNeeded(true); + } else { + currentTab.scrollIntoView(); + } + } + } + + hideMenuItems() { + // Hide items in options menu. + if ( + this.cchConfig.hide_help || + this.cchConfig.hide_config || + this.cchConfig.hide_unused + ) { + const localized = (item, string) => { + let localString = this.hass.localize( + `ui.panel.lovelace.menu.${string}` + ); + return ( + item.innerHTML.includes(localString) || + item.getAttribute("aria-label") == localString + ); + }; + this.buttons.options + .querySelector("paper-listbox") + .querySelectorAll("paper-item") + .forEach(item => { + if ( + (this.cchConfig.hide_help && localized(item, "help")) || + (this.cchConfig.hide_unused && + localized(item, "unused_entities")) || + (this.cchConfig.hide_config && localized(item, "configure_ui")) + ) { + item.parentNode.removeChild(item); + } + }); + } + } + + insertEditMenu(tabs, disabled) { + 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"); + show_tabs.setAttribute("id", "show_tabs"); + show_tabs.addEventListener("click", () => { + for (let i = 0; i < tabs.length; i++) { + tabs[i].style.removeProperty("display"); + } + }); + show_tabs.innerHTML = "Show all tabs"; + this.insertMenuItem( + this.buttons.options.querySelector("paper-listbox"), + show_tabs + ); + } + + // Add menu item to open CCH settings. + let cchSettings = document.createElement("paper-item"); + cchSettings.setAttribute("id", "cch_settings"); + cchSettings.addEventListener("click", () => this.showEditor()); + cchSettings.innerHTML = "CCH Settings"; + this.insertMenuItem( + this.buttons.options.querySelector("paper-listbox"), + cchSettings + ); + if (!disabled) this.hideMenuItems(); + } + } + + removeStyles(tabContainer, tabs, { style }) { + this.root.querySelector("app-header").style.backgroundColor = "#455a64"; + this.root.querySelectorAll("[id^='cch']").forEach(style => { + style.remove(); + }); + if (this.cchConfig.tab_css) { + for (let [key, value] of Object.entries(this.cchConfig.tab_css)) { + key = this.getViewIndex(key); + value = value.replace(/: /g, ":").replace(/; /g, ";"); + let css = tabs[key].style.cssText + .replace(/: /g, ":") + .replace(/; /g, ";"); + tabs[key].style.cssText = css.replace(value, ""); + } + } + if (this.cchConfig.header_css) { + let value = this.cchConfig.header_css + .replace(/: /g, ":") + .replace(/; /g, ";"); + let css = style.cssText.replace(/: /g, ":").replace(/; /g, ";"); + style.cssText = css.replace(value, ""); + } + if (tabContainer) { + tabContainer.style.marginLeft = ""; + tabContainer.style.marginRight = ""; + } + this.view.style = ""; + for (let i = 0; i < tabs.length; i++) { + tabs[i].style.color = ""; + } + if (this.cchConfig.edit_mode_show_tabs) { + for (let i = 0; i < tabs.length; i++) { + tabs[i].style.removeProperty("display"); + } + } + let viewStyle = document.createElement("style"); + viewStyle.setAttribute("id", "cch_view_styling"); + viewStyle.innerHTML = ` + hui-view { + min-height: 100vh; + } + hui-panel-view { + min-height: calc(100vh - 52px); + } + `; + this.root.appendChild(viewStyle); + } + + styleHeader(tabContainer, tabs) { + // Fix for old background config option. + if (typeof this.cchConfig.background == "boolean") { + this.cchConfig.background = ""; + } + this.prevColor.background = + this.cchConfig.background || + getComputedStyle(document.body).getPropertyValue("--cch-background") || + getComputedStyle(document.body).getPropertyValue("--primary-color"); + let statusBarColor = + this.cchConfig.statusbar_color || this.prevColor.background; + // Match mobile status bar color to header color. + let themeColor = document.querySelector('[name="theme-color"]'); + let themeColorApple = + document.querySelector( + '[name="apple-mobile-web-app-status-bar-style"]' + ) || document.createElement("meta"); + colorStatusBar(statusBarColor); + // If browser is idle or in background sometimes theme-color needs reset. + let observeStatus = new MutationObserver(() => { + if (themeColor.content != statusBarColor) colorStatusBar(statusBarColor); + }); + if (this.firstRun) { + observeStatus.observe(themeColor, { + attributes: true, + attributeFilter: ["content"] + }); + } + + // Adjust view size & padding for new header size. + if (!this.cchConfig.header || this.cchConfig.kiosk_mode) { + this.header.style.display = "none"; + this.view.style.minHeight = "100vh"; + if ( + this.frontendVersion >= 20190911 && + !this.root.querySelector("#cch_view_styling") + ) { + let viewStyle = document.createElement("style"); + viewStyle.setAttribute("id", "cch_view_styling"); + viewStyle.innerHTML = ` + hui-view { + ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} + } + hui-panel-view { + ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} + } + `; + this.root.appendChild(viewStyle); + } + } else { + this.view.style.minHeight = "100vh"; + this.view.style.marginTop = "-48.5px"; + 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 && + !this.root.querySelector("#cch_view_styling") + ) { + let viewStyle = document.createElement("style"); + viewStyle.setAttribute("id", "cch_view_styling"); + viewStyle.innerHTML = ` + hui-view { + margin-top: -48.5px; + padding-top: 52px; + min-height: 100vh; + ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} + } + hui-panel-view { + margin-top: -52px; + padding-top: 52px; + min-height: calc(100vh - 52px); + ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} + } + `; + this.root.appendChild(viewStyle); + } + } + + // Match sidebar elements to header's size. + if (this.newSidebar && this.cchConfig.compact_header) { + let sidebar = this.main.shadowRoot.querySelector("ha-sidebar").shadowRoot; + sidebar.querySelector(".menu").style = "height:49px;"; + sidebar.querySelector("paper-listbox").style = + "height:calc(100% - 180px);"; + } + + // Current tab icon color. + let conditionalTabs = this.cchConfig.conditional_styles + ? JSON.stringify(this.cchConfig.conditional_styles).includes("tab") + : false; + if ( + !this.root.querySelector("#cch_iron_selected") && + !this.editMode && + !conditionalTabs && + tabContainer + ) { + let style = document.createElement("style"); + style.setAttribute("id", "cch_iron_selected"); + style.innerHTML = ` + .iron-selected { + ${ + this.cchConfig.active_tab_color + ? `color: ${`${ + this.cchConfig.active_tab_color + } !important`}` + : "var(--cch-active-tab-color)" + } + } + `; + tabContainer.appendChild(style); + } + + // Style current tab indicator. + let indicator = this.cchConfig.tab_indicator_color; + if ( + indicator && + !this.root.querySelector("#cch_header_colors") && + !this.editMode + ) { + let style = document.createElement("style"); + style.setAttribute("id", "cch_header_colors"); + style.innerHTML = ` + paper-tabs { ${ - cchConfig.active_tab_color - ? `color: ${cchConfig.active_tab_color + " !important"}` - : "var(--cch-active-tab-color)" + indicator + ? `--paper-tabs-selection-bar-color: ${indicator} !important` + : "var(--cch-tab-indicator-color) !important" } } `; - tabContainer.appendChild(style); - } - let all_tabs_color = cchConfig.all_tabs_color || "var(--cch-all-tabs-color)"; - if ( - (cchConfig.tab_color && Object.keys(cchConfig.tab_color).length) || - all_tabs_color - ) { - for (let i = 0; i < tabs.length; i++) { - tabs[i].style.color = cchConfig.tab_color[i] || all_tabs_color; + this.root.appendChild(style); + } + + // Tab's icon color. + let all_tabs_color = + this.cchConfig.all_tabs_color || "var(--cch-all-tabs-color)"; + if ( + (this.cchConfig.tab_color && + Object.keys(this.cchConfig.tab_color).length) || + all_tabs_color + ) { + for (let i = 0; i < tabs.length; i++) { + tabs[i].style.color = this.cchConfig.tab_color[i] || all_tabs_color; + } + } + + // Add custom css. + if (this.cchConfig.tab_css) { + for (let [key, value] of Object.entries(this.cchConfig.tab_css)) { + key = this.getViewIndex(key); + if (tabs[key]) tabs[key].style.cssText += value; + } + } + if (this.cchConfig.header_css) + this.header.style.cssText += this.cchConfig.header_css; + if (this.cchConfig.view_css && this.frontendVersion < 20190911) { + this.view.style.cssText += this.cchConfig.view_css; + } + + if (tabContainer) { + // Shift the header up to hide unused portion. + this.root.querySelector("app-toolbar").style.marginTop = this.cchConfig + .compact_header + ? "-64px" + : ""; + + tabs.forEach(({ style }) => { + style.marginTop = "-1px"; + }); + + // Show/hide tab navigation chevrons. + if (!this.cchConfig.chevrons) { + let chevron = tabContainer.shadowRoot.querySelectorAll( + '[icon^="paper-tabs:chevron"]' + ); + chevron[0].style.display = "none"; + chevron[1].style.display = "none"; + } else { + // Remove space taken up by "not-visible" chevron. + let style = document.createElement("style"); + style.setAttribute("id", "cch_chevron"); + style.innerHTML = ` + .not-visible { + display:none; + } + `; + tabContainer.shadowRoot.appendChild(style); + } + } + function colorStatusBar(statusBarColor) { + themeColor = document.querySelector("meta[name=theme-color]"); + themeColor.setAttribute("content", statusBarColor); + themeColor.setAttribute("default-content", statusBarColor); + if ( + !document.querySelector( + '[name="apple-mobile-web-app-status-bar-style"]' + ) + ) { + themeColorApple.name = "apple-mobile-web-app-status-bar-style"; + themeColorApple.content = statusBarColor; + document.getElementsByTagName("head")[0].appendChild(themeColorApple); + } else { + themeColorApple.setAttribute("content", statusBarColor); + } } } - if (tabContainer) { - // Shift the header up to hide unused portion. - root.querySelector("app-toolbar").style.marginTop = cchConfig.compact_header - ? "-64px" - : ""; + styleButtons({ length }, tabContainer) { + let topMargin = + length > 0 && this.cchConfig.compact_header ? "margin-top:111px;" : ""; + let topMarginMenu = + length > 0 && this.cchConfig.compact_header ? "margin-top:115px;" : ""; + // Reverse buttons object so "menu" is first in the overflow menu. + this.buttons = this.reverseObject(this.buttons); + for (const button in this.buttons) { + if (!this.buttons[button]) continue; + if (button == "options" && this.cchConfig[button] == "overflow") { + this.cchConfig[button] = "show"; + } + let buttonStyle = ` + z-index:1; + ${ + button == "menu" + ? `padding: 8px 0; margin-bottom:5px; ${topMarginMenu}` + : "padding: 8px;" + } + ${ + button == "voice" && this.cchConfig["voice"] == "clock" + ? "width: 100px; padding:4px;" + : "" + } + ${button == "menu" ? "" : topMargin} + ${button == "options" ? "margin-right:-5px;" : ""} + `; + if ( + this.cchConfig[button] == "show" || + this.cchConfig[button] == "clock" + ) { + if (button == "menu") { + let paperIconButton = this.buttons[button].querySelector( + "paper-icon-button" + ) + ? this.buttons[button].querySelector("paper-icon-button") + : this.buttons[button].shadowRoot.querySelector( + "paper-icon-button" + ); + if (!paperIconButton) continue; + paperIconButton.style.cssText = buttonStyle; + } else { + this.buttons[button].style.cssText = buttonStyle; + } + } else if (this.cchConfig[button] == "overflow") { + const menu_items = this.buttons.options.querySelector("paper-listbox"); + let paperIconButton = this.buttons[button].querySelector( + "paper-icon-button" + ) + ? this.buttons[button].querySelector("paper-icon-button") + : this.buttons[button].shadowRoot.querySelector("paper-icon-button"); + if (paperIconButton && paperIconButton.hasAttribute("hidden")) { + continue; + } + const id = `menu_item_${button}`; + if (!menu_items.querySelector(`#${id}`)) { + const wrapper = document.createElement("paper-item"); + wrapper.setAttribute("id", id); + wrapper.innerText = this.getTranslation(button); + wrapper.appendChild(this.buttons[button]); + wrapper.addEventListener("click", () => { + paperIconButton.click(); + }); + paperIconButton.style.pointerEvents = "none"; + this.insertMenuItem(menu_items, wrapper); + if (button == "notifications" && !this.newSidebar) { + let style = document.createElement("style"); + style.innerHTML = ` + .indicator { + top: 5px; + right: 0px; + width: 10px; + height: 10px; + ${ + this.cchConfig.notify_indicator_color + ? `background-color:${ + this.cchConfig.notify_indicator_color + }` + : "" + } + } + .indicator > div{ + display:none; + } + `; + paperIconButton.parentNode.appendChild(style); + } + } + } else if (this.cchConfig[button] == "hide") { + this.buttons[button].style.display = "none"; + } + // Hide menu button if hiding the sidebar. + if ( + this.newSidebar && + (this.cchConfig.kiosk_mode || this.cchConfig.disable_sidebar) + ) { + this.buttons.menu.style.display = "none"; + } + } - if (!cchConfig.chevrons) { - // Hide chevrons. - let chevron = tabContainer.shadowRoot.querySelectorAll( - '[icon^="paper-tabs:chevron"]' - ); - chevron[0].style.display = "none"; - chevron[1].style.display = "none"; - } else { - // Remove space taken up by "not-visible" chevron. + // Remove empty space taken up by hidden menu button. + if (this.buttons.menu && this.newSidebar && this.firstRun) { + new MutationObserver(() => { + if (this.buttons.menu.style.visibility == "hidden") { + this.buttons.menu.style.display = "none"; + } else { + this.buttons.menu.style.display = ""; + } + this.tabContainerMargin(tabContainer); + }).observe(this.buttons.menu, { + attributes: true, + attributeFilter: ["style"] + }); + } + + // Use color vars set in HA theme. + this.buttons.menu.style.color = "var(--cch-button-color-menu)"; + if (!this.newSidebar) { + this.buttons.notifications.style.color = + "var(--cch-button-color-notifications)"; + } + if (this.buttons.voice) this.buttons.voice.style.color = "var(--cch-button-color-voice)"; + this.buttons.options.style.color = "var(--cch-button-color-options)"; + if (this.cchConfig.all_buttons_color) { + this.root.querySelector("app-toolbar").style.color = + this.cchConfig.all_buttons_color || "var(--cch-all-buttons-color)"; + } + + // Use colors set in CCH config. + for (const button in this.buttons) { + if (this.cchConfig.button_color[button]) { + this.buttons[button].style.color = this.cchConfig.button_color[button]; + } + } + + // Notification indicator's color for HA 0.96 and above. + if ( + this.newSidebar && + this.cchConfig.menu != "hide" && + !this.buttons.menu.shadowRoot.querySelector("#cch_dot") + ) { let style = document.createElement("style"); - style.setAttribute("id", "cch_chevron"); + style.setAttribute("id", "cch_dot"); + let indicator = + this.cchConfig.notify_indicator_color || + getComputedStyle(this.header).getPropertyValue( + "--cch-tab-indicator-color" + ) || + ""; + let border = getComputedStyle(this.header) + .getPropertyValue("background") + .includes("url") + ? "border-color: transparent !important" + : `border-color: ${getComputedStyle(this.header).getPropertyValue( + "background-color" + )} !important;`; style.innerHTML = ` - .not-visible { + .dot { + ${topMargin} + z-index: 2; + ${indicator ? `background: ${indicator} !important` : ""} + ${border} + } + `; + this.buttons.menu.shadowRoot.appendChild(style); + } else if ( + // Notification indicator's color for HA 0.95 and below. + this.cchConfig.notify_indicator_color && + this.cchConfig.notifications == "show" && + !this.newSidebar + ) { + let style = document.createElement("style"); + style.innerHTML = ` + .indicator { + background-color:${this.cchConfig.notify_indicator_color || + "var(--cch-notify-indicator-color)"} !important; + color: ${this.cchConfig.notify_text_color || + "var(--cch-notify-text-color), var(--primary-text-color)"}; + } + `; + this.buttons.notifications.shadowRoot.appendChild(style); + } + + // Add buttons's custom css. + let buttonCss = this.cchConfig.button_css; + if (buttonCss) { + for (const [key, value] of Object.entries(buttonCss)) { + if (!this.buttons[key]) { + continue; + } else { + this.buttons[key].style.cssText += value; + } + } + } + } + + getTranslation(button) { + switch (button) { + case "notifications": + return this.hass.localize("ui.notification_drawer.title"); + default: + return button.charAt(0).toUpperCase() + button.slice(1); + } + } + + defaultTab(tabs, tabContainer) { + let firstTab = tabs.indexOf(tabs.filter(tab => tab.style.display == "")[0]); + let default_tab = this.cchConfig.default_tab; + if (typeof default_tab == "object" && !default_tab.length) return; + let template = this.cchConfig.default_tab_template; + if ((default_tab || template) && tabContainer) { + if (template) default_tab = this.templateEval(template, this.hass.states); + default_tab = this.getViewIndex(default_tab); + let activeTab = tabs.indexOf( + tabContainer.querySelector(".iron-selected") + ); + if ( + activeTab != default_tab && + activeTab == firstTab && + (!this.cchConfig.redirect || + (this.cchConfig.redirect && + tabs[default_tab].style.display != "none")) + ) { + tabs[default_tab].click(); + } + } + } + + sidebarMod() { + let menu = this.buttons.menu.querySelector("paper-icon-button"); + let sidebar = this.main.shadowRoot.querySelector("app-drawer"); + + // HA 0.95 and below + if (!this.newSidebar) { + if (!this.cchConfig.sidebar_swipe || this.cchConfig.kiosk_mode) { + sidebar.removeAttribute("swipe-open"); + } + if ( + (this.cchConfig.sidebar_closed || this.cchConfig.kiosk_mode) && + !this.sidebarClosed + ) { + if (sidebar.hasAttribute("opened")) menu.click(); + this.sidebarClosed = true; + } + // HA 0.96 and above + } else if (this.cchConfig.disable_sidebar || this.cchConfig.kiosk_mode) { + sidebar.style.display = "none"; + sidebar.addEventListener( + "mouseenter", + event => { + event.stopPropagation(); + }, + true + ); + let style = document.createElement("style"); + style.type = "text/css"; + style.appendChild( + document.createTextNode( + ":host(:not([expanded])) {width: 0px !important;}" + ) + ); + this.main.shadowRoot + .querySelector("ha-sidebar") + .shadowRoot.appendChild(style); + + style = document.createElement("style"); + style.type = "text/css"; + style.appendChild( + document.createTextNode(":host {--app-drawer-width: 0px !important;}") + ); + this.main.shadowRoot.appendChild(style); + } + } + + hideTabs(tabContainer, tabs) { + let hidden_tabs = String(this.cchConfig.hide_tabs).length + ? String(this.cchConfig.hide_tabs) + .replace(/\s+/g, "") + .split(",") + : null; + let shown_tabs = String(this.cchConfig.show_tabs).length + ? String(this.cchConfig.show_tabs) + .replace(/\s+/g, "") + .split(",") + : null; + + // Set the tab config source. + if (!hidden_tabs && shown_tabs) { + let all_tabs = []; + shown_tabs = this.buildRanges(shown_tabs); + for (let i = 0; i < tabs.length; i++) all_tabs.push(i); + // Invert shown_tabs to hidden_tabs. + hidden_tabs = all_tabs.filter(el => !shown_tabs.includes(el)); + } else { + hidden_tabs = this.buildRanges(hidden_tabs); + } + + // Hide tabs. + for (const tab of hidden_tabs) { + if (!tabs[tab]) continue; + tabs[tab].style.display = "none"; + } + + if (this.cchConfig.redirect && tabContainer) { + const activeTab = tabContainer.querySelector("paper-tab.iron-selected"); + const activeTabIndex = tabs.indexOf(activeTab); + // Is the current tab hidden and is there at least one tab is visible. + if ( + hidden_tabs.includes(activeTabIndex) && + hidden_tabs.length != tabs.length + ) { + let i = 0; + // Find the first visible tab and navigate. + while (hidden_tabs.includes(i)) { + i++; + } + tabs[i].click(); + } + } + return hidden_tabs; + } + + insertMenuItem(menu_items, element) { + let first_item = menu_items.querySelector("paper-item"); + if (!menu_items.querySelector(`#${element.id}`)) { + first_item.parentNode.insertBefore(element, first_item); + } + } + + insertClock(button) { + if (!this.buttons[button]) return; + const clock_button = this.buttons[button].querySelector("paper-icon-button") + ? this.buttons[button] + : this.buttons[button].shadowRoot; + const clockIcon = + clock_button.querySelector("paper-icon-button") || this.buttons[button]; + const clockIronIcon = + clockIcon.querySelector("iron-icon") || + clockIcon.shadowRoot.querySelector("iron-icon"); + const clockWidth = + (this.cchConfig.clock_format == 12 && this.cchConfig.clock_am_pm) || + this.cchConfig.clock_date + ? 105 + : 80; + + if ( + !this.newSidebar && + this.cchConfig.notifications == "clock" && + this.cchConfig.clock_date && + !this.buttons.notifications.shadowRoot.querySelector("#cch_indicator") + ) { + let style = document.createElement("style"); + style.setAttribute("id", "cch_indicator"); + style.innerHTML = ` + .indicator { + top: unset; + bottom: -3px; + right: 0px; + width: 90%; + height: 3px; + border-radius: 0; + ${ + this.cchConfig.notify_indicator_color + ? `background-color:${this.cchConfig.notify_indicator_color}` + : "" + } + } + .indicator > div{ display:none; } `; - tabContainer.shadowRoot.appendChild(style); + this.buttons.notifications.shadowRoot.appendChild(style); } - } -} -function styleButtons(tabs) { - let topMargin = - tabs.length > 0 && cchConfig.compact_header ? "margin-top:111px;" : ""; - buttons = reverseObject(buttons); - if ( - newSidebar && - cchConfig.menu != "hide" && - !buttons.menu.shadowRoot.querySelector('[id="cch_dot"]') - ) { - let style = document.createElement("style"); - style.setAttribute("id", "cch_dot"); - let indicator = - cchConfig.notify_indicator_color || - getComputedStyle(header).getPropertyValue("--cch-tab-indicator-color") || - ""; - let border = getComputedStyle(header) - .getPropertyValue("background") - .includes("url") - ? "border-color: transparent !important" - : `border-color: ${getComputedStyle(header).getPropertyValue( - "background-color" - )} !important;`; - style.innerHTML = ` - .dot { - ${topMargin} - z-index: 2; - ${indicator ? `background: ${indicator} !important` : ""} - ${border} - } - `; - buttons.menu.shadowRoot.appendChild(style); - } - for (const button in buttons) { - if (!buttons[button]) continue; - if (button == "options" && cchConfig[button] == "overflow") { - cchConfig[button] = "show"; + let clockElement = clockIronIcon.parentNode.getElementById("cch_clock"); + if (this.cchConfig.menu == "clock") { + this.buttons.menu.style.marginTop = this.cchConfig.compact_header + ? "111px" + : ""; + this.buttons.menu.style.zIndex = "1"; } - if (cchConfig[button] == "show" || cchConfig[button] == "clock") { - if (button == "menu") { - let paperIconButton = buttons[button].querySelector("paper-icon-button") - ? buttons[button].querySelector("paper-icon-button") - : buttons[button].shadowRoot.querySelector("paper-icon-button"); - if (!paperIconButton) continue; - paperIconButton.style.cssText = ` - z-index:1; - ${topMargin} - ${button == "options" ? "margin-right:-5px; padding:0;" : ""} - `; - } else { - buttons[button].style.cssText = ` - z-index:1; - ${topMargin} - ${button == "options" ? "margin-right:-5px; padding:0;" : ""} - `; - } - } else if (cchConfig[button] == "overflow") { - const menu_items = buttons.options.querySelector("paper-listbox"); - let paperIconButton = buttons[button].querySelector("paper-icon-button") - ? buttons[button].querySelector("paper-icon-button") - : buttons[button].shadowRoot.querySelector("paper-icon-button"); - if (paperIconButton.hasAttribute("hidden")) { - continue; - } - const id = `menu_item_${button}`; - if (!menu_items.querySelector(`[id="${id}"]`)) { - const wrapper = document.createElement("paper-item"); - wrapper.setAttribute("id", id); - wrapper.innerText = getTranslation(button); - wrapper.appendChild(buttons[button]); - wrapper.addEventListener("click", () => { - paperIconButton.click(); - }); - paperIconButton.style.pointerEvents = "none"; - insertMenuItem(menu_items, wrapper); - if (button == "notifications" && !newSidebar) { - let style = document.createElement("style"); - style.innerHTML = ` - .indicator { - top: 5px; - right: 0px; - width: 10px; - height: 10px; - ${ - cchConfig.notify_indicator_color - ? `background-color:${cchConfig.notify_indicator_color}` - : "" - } - } - .indicator > div{ - display:none; - } + if (!clockElement) { + clockIcon.style.cssText = ` + margin-right:-5px; + width:${clockWidth}px; + text-align: center; `; - paperIconButton.parentNode.appendChild(style); + clockElement = document.createElement("p"); + clockElement.setAttribute("id", "cch_clock"); + let clockAlign = "center"; + let padding = ""; + let fontSize = ""; + if (this.cchConfig.clock_date && this.cchConfig.menu == "clock") { + clockAlign = "left"; + padding = "margin-right:-20px"; + fontSize = "font-size:12pt"; + } else if (this.cchConfig.clock_date) { + clockAlign = "right"; + padding = "margin-left:-20px"; + fontSize = "font-size:12pt"; + } + clockElement.style.cssText = ` + margin-top: ${this.cchConfig.clock_date ? "-4px" : "2px"}; + text-align: ${clockAlign}; + ${padding}; + ${fontSize}; + `; + clockIronIcon.parentNode.insertBefore(clockElement, clockIronIcon); + clockIronIcon.style.display = "none"; + let style = document.createElement("style"); + style.setAttribute("id", "cch_clock"); + style.innerHTML = ` + time { + ${this.cchConfig.time_css} + } + date { + ${this.cchConfig.date_css} + } + `; + clockIronIcon.parentNode.insertBefore(style, clockIronIcon); + } + + const clockFormat = { + hour12: this.cchConfig.clock_format != 24, + hour: "2-digit", + minute: "2-digit" + }; + this.updateClock(clockElement, clockFormat); + } + + updateClock(clock, clockFormat) { + let date = new Date(); + let seconds = date.getSeconds(); + let locale = this.cchConfig.date_locale || this.hass.language; + let time = date.toLocaleTimeString([], clockFormat); + let options = { + weekday: "short", + month: "2-digit", + day: "2-digit" + }; + date = this.cchConfig.clock_date + ? `
${date.toLocaleDateString(locale, options)}` + : ""; + if (!this.cchConfig.clock_am_pm && this.cchConfig.clock_format == 12) { + clock.innerHTML = `${date}`; + } else { + clock.innerHTML = `${date}`; + } + window.setTimeout(() => { + this.updateClock(clock, clockFormat); + }, (60 - seconds) * 1000); + } + + // Abandon all hope, ye who enter here. + conditionalStyling(tabs) { + let _hass = document.querySelector("home-assistant").hass; + const conditional_styles = this.cchConfig.conditional_styles; + let tabContainer = tabs[0] ? tabs[0].parentNode : ""; + let styling = []; + + if (Array.isArray(conditional_styles)) { + for (let i = 0; i < conditional_styles.length; i++) { + styling.push(Object.assign({}, conditional_styles[i])); + } + } else { + styling.push(Object.assign({}, conditional_styles)); + } + + function exists(configItem) { + return configItem !== undefined && configItem !== null; + } + + function notificationCount() { + if (this.newSidebar) { + let badge = this.main.shadowRoot + .querySelector("ha-sidebar") + .shadowRoot.querySelector("span.notification-badge"); + if (!badge) return 0; + else return parseInt(badge.innerHTML); + } + let i = 0; + let drawer = this.root + .querySelector("hui-notification-drawer") + .shadowRoot.querySelector(".notifications"); + for (let notification of drawer.querySelectorAll(".notification")) { + if (notification.style.display !== "none") i++; + } + return i; + } + + for (let i = 0; i < styling.length; i++) { + let template = styling[i].template; + let condition = styling[i].condition; + + if (template) { + if (!template.length) template = [template]; + template.forEach(template => { + this.templates(template, tabs, _hass, this.header); + }); + } else if (condition) { + let entity = styling[i].entity; + if (_hass.states[entity] == undefined && entity !== "notifications") { + console.log(`CCH conditional styling: ${entity} does not exist.`); + continue; + } + let entState = + entity == "notifications" + ? notificationCount() + : _hass.states[entity].state; + let condState = condition.state; + let above = condition.above; + let below = condition.below; + + let toStyle = + (exists(condState) && entState == condState) || + (exists(above) && + exists(below) && + entState > above && + entState < below) || + (exists(above) && entState > above) || + (exists(below) && entState < below); + + let tabIndex = styling[i].tab ? Object.keys(styling[i].tab)[0] : null; + let tabCondition = styling[i].tab ? styling[i].tab[tabIndex] : null; + let tabElem = tabs[this.getViewIndex(tabIndex)]; + let tabkey = `tab_${this.getViewIndex(tabIndex)}`; + let button = styling[i].button + ? Object.keys(styling[i].button)[0] + : null; + let background = styling[i].background; + + // Conditionally style tabs. + if (toStyle && exists(tabIndex) && tabElem) { + if (tabCondition.hide) tabElem.style.display = "none"; + if (tabCondition.color) { + if (this.prevColor[tabkey] == undefined) { + Object.assign(this.prevColor, { + [tabkey]: window + .getComputedStyle(tabElem, null) + .getPropertyValue("color") + }); + } + tabElem.style.color = tabCondition.color; + } + if (tabCondition.on_icon) { + tabElem + .querySelector("ha-icon") + .setAttribute("icon", tabCondition.on_icon); + } + } else if (!toStyle && exists(tabIndex) && tabElem) { + if (tabCondition.hide) { + tabElem.style.display = ""; + } + if (tabCondition.color && this.prevColor[tabkey]) { + tabElem.style.color = this.prevColor[tabkey]; + } + if (tabCondition.off_icon) { + tabElem + .querySelector("ha-icon") + .setAttribute("icon", tabCondition.off_icon); + } + } + + if (toStyle && button) { + if (!this.buttons[button]) continue; + let buttonCondition = styling[i].button[button]; + let buttonElem = this.buttons[button].querySelector( + "paper-icon-button" + ) + ? this.buttons[button].querySelector("paper-icon-button") + : this.buttons[button].shadowRoot.querySelector( + "paper-icon-button" + ); + if (buttonCondition.hide) { + buttonElem.style.display = "none"; + } + if (buttonCondition.color) { + if (this.prevColor.button == undefined) this.prevColor.button = {}; + if (this.prevColor.button[button] == undefined) { + this.prevColor.button[button] = window + .getComputedStyle(buttonElem, null) + .getPropertyValue("color"); + } + buttonElem.style.color = buttonCondition.color; + } + if (buttonCondition.on_icon) { + let icon = + buttonElem.querySelector("iron-icon") || + buttonElem.shadowRoot.querySelector("iron-icon"); + icon.setAttribute("icon", buttonCondition.on_icon); + } + } else if (!toStyle && button) { + let buttonCondition = styling[i].button[button]; + let buttonElem = this.buttons[button].querySelector( + "paper-icon-button" + ) + ? this.buttons[button].querySelector("paper-icon-button") + : this.buttons[button].shadowRoot.querySelector( + "paper-icon-button" + ); + if (buttonCondition.hide) { + buttonElem.style.display = ""; + } + if ( + buttonCondition.color && + this.prevColor.button && + this.prevColor.button[button] + ) { + buttonElem.style.color = this.prevColor.button[button]; + } + if (buttonCondition.off_icon) { + let icon = + buttonElem.querySelector("iron-icon") || + buttonElem.shadowRoot.querySelector("iron-icon"); + icon.setAttribute("icon", buttonCondition.off_icon); + } + } + + // Conditionally style background. + if (toStyle && background) { + if (this.prevColor.background == undefined) { + this.prevColor.background = window + .getComputedStyle(this.header, null) + .getPropertyValue("background"); + } + this.header.style.background = styling[i].background; + } else if (!toStyle && background) { + this.header.style.background = this.prevColor.background; } } - } else if (cchConfig[button] == "hide") { - buttons[button].style.display = "none"; } - if (newSidebar && (cchConfig.kiosk_mode || cchConfig.disable_sidebar)) { - buttons.menu.style.display = "none"; + this.tabContainerMargin(tabContainer); + } + + templates(template, tabs, _hass, { style }) { + let states = _hass.states; + for (const condition in template) { + if (condition == "tab") { + for (const tab in template[condition]) { + let tempCond = template[condition][tab]; + if (!tempCond.length) tempCond = [tempCond]; + tempCond.forEach(templateObj => { + let tabIndex = this.getViewIndex(Object.keys(template[condition])); + let styleTarget = Object.keys(templateObj); + let tabTemplate = templateObj[styleTarget]; + let tabElement = tabs[tabIndex]; + if (styleTarget == "icon") { + tabElement + .querySelector("ha-icon") + .setAttribute("icon", this.templateEval(tabTemplate, states)); + } else if (styleTarget == "color") { + tabElement.style.color = this.templateEval(tabTemplate, states); + } else if (styleTarget == "display") { + this.templateEval(tabTemplate, states) == "show" + ? (tabElement.style.display = "") + : (tabElement.style.display = "none"); + } + }); + } + } else if (condition == "button") { + for (const button in template[condition]) { + let tempCond = template[condition][button]; + if (!tempCond.length) tempCond = [tempCond]; + tempCond.forEach(templateObj => { + let buttonName = Object.keys(template[condition]); + if (this.newSidebar && buttonName == "notifications") return; + let styleTarget = Object.keys(templateObj); + let buttonElem = this.buttons[buttonName]; + let tempCond = templateObj[styleTarget]; + let iconTarget = buttonElem.querySelector("paper-icon-button") + ? buttonElem.querySelector("paper-icon-button") + : buttonElem.shadowRoot.querySelector("paper-icon-button"); + if (styleTarget == "icon") { + iconTarget.setAttribute( + "icon", + this.templateEval(tempCond, states) + ); + } else if (styleTarget == "color") { + let tar = + iconTarget.querySelector("iron-icon") || + iconTarget.shadowRoot.querySelector("iron-icon"); + tar.style.color = this.templateEval(tempCond, states); + } else if (styleTarget == "display") { + this.templateEval(tempCond, states) == "show" + ? (buttonElem.style.display = "") + : (buttonElem.style.display = "none"); + } + }); + } + } else if (condition == "background") { + style.background = this.templateEval(template[condition], states); + } } } - // Use color vars set in HA theme. - buttons.menu.style.color = "var(--cch-button-color-menu)"; - if (!newSidebar) { - buttons.notifications.style.color = "var(--cch-button-color-notifications)"; - } - buttons.voice.style.color = "var(--cch-button-color-voice)"; - buttons.options.style.color = "var(--cch-button-color-options)"; - if (cchConfig.all_buttons_color) { - root.querySelector("app-toolbar").style.color = - cchConfig.all_buttons_color || "var(--cch-all-buttons-color)"; - } - - // Use colors set in CCH config. - for (const button in buttons) { - if (cchConfig.button_color[button]) { - buttons[button].style.color = cchConfig.button_color[button]; - } - } - - if (cchConfig.notify_indicator_color && cchConfig.notifications == "show") { - let style = document.createElement("style"); - style.innerHTML = ` - .indicator { - background-color:${cchConfig.notify_indicator_color || - "var(--cch-notify-indicator-color)"} !important; - color: ${cchConfig.notify_text_color || - "var(--cch-notify-text-color), var(--primary-text-color)"}; + // Get range (e.g., "5 to 9") and build (5,6,7,8,9). + buildRanges(array) { + let ranges = []; + if (!array) return []; + const sortNumber = (a, b) => a - b; + const range = (start, end) => + new Array(end - start + 1).fill(undefined).map((_, i) => i + start); + for (let i in array) { + if (typeof array[i] == "string" && array[i].includes("to")) { + let split = array[i].split("to"); + if (parseInt(split[1]) > parseInt(split[0])) { + ranges.push(range(parseInt(split[0]), parseInt(split[1]))); + } else { + ranges.push(range(parseInt(split[1]), parseInt(split[0]))); + } + } else if (isNaN(array[i])) { + let views = this.lovelace.config.views; + for (let view in views) { + if ( + views[view]["title"] == array[i] || + views[view]["path"] == array[i] + ) { + ranges.push(parseInt(view)); } - `; - if (!newSidebar) buttons.notifications.shadowRoot.appendChild(style); + } + } else { + ranges.push(parseInt(array[i])); + } + } + return ranges.flat().sort(sortNumber); } -} -function getTranslation(button) { - switch (button) { - case "notifications": - return hass.localize("ui.notification_drawer.title"); - default: - return button.charAt(0).toUpperCase() + button.slice(1); + showEditor() { + window.scrollTo(0, 0); + if (!this.root.querySelector("ha-app-layout editor")) { + const container = document.createElement("editor"); + const nest = document.createElement("div"); + nest.style.cssText = ` + padding: 20px; + max-width: 600px; + margin: 15px auto; + background: var(--paper-card-background-color); + border: 6px solid var(--paper-card-background-color); + `; + container.style.cssText = ` + width: 100%; + min-height: 100%; + box-sizing: border-box; + position: absolute; + background: var(--background-color, grey); + z-index: 2; + padding: 5px; + `; + this.root + .querySelector("ha-app-layout") + .insertBefore(container, this.view); + container.appendChild(nest); + nest.appendChild(document.createElement("compact-custom-header-editor")); + } } -} -function defaultTab(tabs, tabContainer) { - if (cchConfig.default_tab && !defaultTabRedirect && tabContainer) { - let default_tab = cchConfig.default_tab; - let activeTab = tabs.indexOf(tabContainer.querySelector(".iron-selected")); - if (isNaN(default_tab)) { - let views = lovelace.config.views; + getViewIndex(viewString) { + let views = this.lovelace.config.views; + if (isNaN(viewString)) { for (let view in views) { if ( - views[view]["title"] == default_tab || - views[view]["path"] == default_tab + views[view]["title"] == viewString || + views[view]["path"] == viewString ) { - default_tab = view; + return view; } } - } - if ( - activeTab != default_tab && - activeTab == 0 && - !cchConfig.hide_tabs.includes(default_tab) - ) { - tabs[default_tab].click(); - } - defaultTabRedirect = true; - } -} - -function sidebarMod() { - let menu = buttons.menu.querySelector("paper-icon-button"); - let sidebar = main.shadowRoot.querySelector("app-drawer"); - - if (!newSidebar) { - if (!cchConfig.sidebar_swipe || cchConfig.kiosk_mode) { - sidebar.removeAttribute("swipe-open"); - } - if ((cchConfig.sidebar_closed || cchConfig.kiosk_mode) && !sidebarClosed) { - if (sidebar.hasAttribute("opened")) menu.click(); - sidebarClosed = true; - } - } else if ( - newSidebar && - (cchConfig.disable_sidebar || cchConfig.kiosk_mode) - ) { - sidebar.style.display = "none"; - sidebar.addEventListener( - "mouseenter", - function(event) { - event.stopPropagation(); - }, - true - ); - let style = document.createElement("style"); - style.type = "text/css"; - style.appendChild( - document.createTextNode( - ":host(:not([expanded])) {width: 0px !important;}" - ) - ); - main.shadowRoot.querySelector("ha-sidebar").shadowRoot.appendChild(style); - - style = document.createElement("style"); - style.type = "text/css"; - style.appendChild( - document.createTextNode(":host {--app-drawer-width: 0px !important;}") - ); - main.shadowRoot.appendChild(style); - } -} - -function hideTabs(tabContainer, tabs) { - let hidden_tabs = String(cchConfig.hide_tabs).length - ? String(cchConfig.hide_tabs) - .replace(/\s+/g, "") - .split(",") - : null; - let shown_tabs = String(cchConfig.show_tabs).length - ? String(cchConfig.show_tabs) - .replace(/\s+/g, "") - .split(",") - : null; - - // Set the tab config source. - if (!hidden_tabs && shown_tabs) { - let all_tabs = []; - shown_tabs = buildRanges(shown_tabs); - for (let i = 0; i < tabs.length; i++) all_tabs.push(i); - // Invert shown_tabs to hidden_tabs. - hidden_tabs = all_tabs.filter(el => !shown_tabs.includes(el)); - } else { - hidden_tabs = buildRanges(hidden_tabs); - } - - // Hide tabs. - for (const tab of hidden_tabs) { - if (!tabs[tab]) continue; - tabs[tab].style.display = "none"; - } - - if (cchConfig.redirect && tabContainer) { - const activeTab = tabContainer.querySelector("paper-tab.iron-selected"); - const activeTabIndex = tabs.indexOf(activeTab); - // Is the current tab hidden and is there at least one tab is visible. - if ( - hidden_tabs.includes(activeTabIndex) && - hidden_tabs.length != tabs.length - ) { - let i = 0; - // Find the first visible tab and navigate. - while (hidden_tabs.includes(i)) { - i++; - } - tabs[i].click(); + } else { + return parseInt(viewString); } } - return hidden_tabs; -} -function insertMenuItem(menu_items, element) { - let first_item = menu_items.querySelector("paper-item"); - if (!menu_items.querySelector(`[id="${element.id}"]`)) { - first_item.parentNode.insertBefore(element, first_item); - } -} - -function insertClock(button) { - const clock_button = buttons[button].querySelector("paper-icon-button") - ? buttons[button] - : buttons[button].shadowRoot; - const clockIcon = clock_button.querySelector("paper-icon-button"); - const clockIronIcon = clockIcon.shadowRoot.querySelector("iron-icon"); - const clockWidth = - (cchConfig.clock_format == 12 && cchConfig.clock_am_pm) || - cchConfig.clock_date - ? 110 - : 80; - - if ( - !newSidebar && - cchConfig.notifications == "clock" && - cchConfig.clock_date && - !buttons.notifications.shadowRoot.querySelector('[id="cch_indicator"]') - ) { - let style = document.createElement("style"); - style.setAttribute("id", "cch_indicator"); - style.innerHTML = ` - .indicator { - top: unset; - bottom: -3px; - right: 0px; - width: 90%; - height: 3px; - border-radius: 0; - ${ - cchConfig.notify_indicator_color - ? `background-color:${cchConfig.notify_indicator_color}` - : "" - } - } - .indicator > div{ - display:none; - } - `; - buttons.notifications.shadowRoot.appendChild(style); - } - - let clockElement = clockIronIcon.parentNode.getElementById("cch_clock"); - if (cchConfig.menu == "clock") { - buttons.menu.style.marginTop = cchConfig.compact_header ? "111px" : ""; - buttons.menu.style.zIndex = "1"; - } - if (!clockElement) { - clockIcon.style.cssText = ` - margin-right:-5px; - width:${clockWidth}px; - text-align: center; - `; - clockElement = document.createElement("p"); - clockElement.setAttribute("id", "cch_clock"); - let clockAlign = "center"; - let padding = ""; - let fontSize = ""; - if (cchConfig.clock_date && cchConfig.menu == "clock") { - clockAlign = "left"; - padding = "margin-right:-20px"; - fontSize = "font-size:12pt"; - } else if (cchConfig.clock_date) { - clockAlign = "right"; - padding = "margin-left:-20px"; - fontSize = "font-size:12pt"; + reverseObject(object) { + let newObject = {}; + let keys = []; + for (let key in object) keys.push(key); + for (let i = keys.length - 1; i >= 0; i--) { + let value = object[keys[i]]; + newObject[keys[i]] = value; } - clockElement.style.cssText = ` - margin-top: ${cchConfig.clock_date ? "-4px" : "2px"}; - text-align: ${clockAlign}; - ${padding}; - ${fontSize}; - `; - clockIronIcon.parentNode.insertBefore(clockElement, clockIronIcon); - clockIronIcon.style.display = "none"; + return newObject; } - const clockFormat = { - hour12: cchConfig.clock_format != 24, - hour: "2-digit", - minute: "2-digit" - }; - updateClock(clockElement, clockFormat); -} - -function updateClock(clock, clockFormat) { - let date = new Date(); - let locale = cchConfig.date_locale || hass.language; - let time = date.toLocaleTimeString([], clockFormat); - let options = { - weekday: "short", - month: "2-digit", - day: "2-digit" - }; - date = cchConfig.clock_date - ? `
${date.toLocaleDateString(locale, options)}` - : ""; - if (!cchConfig.clock_am_pm && cchConfig.clock_format == 12) { - clock.innerHTML = time.slice(0, -3) + date; - } else { - clock.innerHTML = time + date; - } - window.setTimeout(() => updateClock(clock, clockFormat), 60000); -} - -// Abandon all hope, ye who enter here. -function conditionalStyling(tabs, header) { - let _hass = document.querySelector("home-assistant").hass; - const conditional_styles = cchConfig.conditional_styles; - let tabContainer = tabs[0] ? tabs[0].parentNode : ""; - let elem, color, bg, hide, onIcon, offIcon, iconElem; - - const styleElements = () => { - if (bg && elem == "background") header.style.background = bg; - else if (color) elem.style.color = color; - if (onIcon && iconElem) iconElem.setAttribute("icon", onIcon); - if (hide && elem !== "background" && !editMode) { - elem.style.display = "none"; - } - }; - - const getElements = (key, elemArray, i, obj, styling) => { - elem = elemArray[key]; - color = styling[i][obj][key].color; - onIcon = styling[i][obj][key].on_icon; - offIcon = styling[i][obj][key].off_icon; - hide = styling[i][obj][key].hide; - if (!prevColor[key]) { - prevColor[key] = window - .getComputedStyle(elem, null) - .getPropertyValue("color"); - } - }; - - let styling = []; - if (Array.isArray(conditional_styles)) { - for (let i = 0; i < conditional_styles.length; i++) { - styling.push(Object.assign({}, conditional_styles[i])); - } - } else { - styling.push(Object.assign({}, conditional_styles)); - } - - for (let i = 0; i < styling.length; i++) { - let template = styling[i].template; - if (template) { - if (!template.length) template = [template]; - template.forEach(template => { - templates(template, tabs, _hass, header); - }); - } else if (conditional_styles) { - let entity = styling[i].entity; - if (_hass.states[entity] == undefined && entity !== "notifications") { - console.log(`CCH conditional styling: ${entity} does not exist.`); - continue; - } - if (entity == "notifications") condState[i] = notifications; - else condState[i] = _hass.states[entity].state; - - if (condState[i] !== prevState[i] || !condState.length) { - prevState[i] = condState[i]; - let above = styling[i].condition.above; - let below = styling[i].condition.below; - - for (const obj in styling[i]) { - let key; - if (styling[i][obj]) { - key = Object.keys(styling[i][obj])[0]; - } - if (obj == "background") { - elem = "background"; - color = styling[i][obj].color; - bg = styling[i][obj]; - iconElem = false; - if (!prevColor[obj]) { - prevColor[obj] = window - .getComputedStyle(header, null) - .getPropertyValue("background"); - } - } else if (obj == "button") { - if (newSidebar && key == "notifications") continue; - getElements(key, buttons, i, obj, styling); - if (key == "menu") { - iconElem = elem - .querySelector("paper-icon-button") - .shadowRoot.querySelector("iron-icon"); - } else { - iconElem = elem.shadowRoot - .querySelector("paper-icon-button") - .shadowRoot.querySelector("iron-icon"); - } - } else if (obj == "tab") { - if (isNaN(key)) { - let views = lovelace.config.views; - for (let view in views) { - if (views[view]["title"] == key || views[view]["path"] == key) { - styling[i][obj][view] = styling[i][obj][key]; - delete styling[i][obj][key]; - key = view; - } - } - } - getElements(key, tabs, i, obj, styling); - iconElem = elem.querySelector("ha-icon"); - } - - if (condState[i] == styling[i].condition.state) { - styleElements(); - } else if ( - above !== undefined && - below !== undefined && - condState[i] > above && - condState[i] < below - ) { - styleElements(); - } else if ( - above !== undefined && - below == undefined && - condState[i] > above - ) { - styleElements(); - } else if ( - above == undefined && - below !== undefined && - condState[i] < below - ) { - styleElements(); - } else { - if (elem !== "background" && hide && elem.style.display == "none") { - elem.style.display = ""; - } - if (bg && elem == "background") { - header.style.background = prevColor[obj]; - } else if ( - obj !== "background" && - obj !== "entity" && - obj !== "condition" - ) { - elem.style.color = prevColor[key]; - } - if (onIcon && offIcon) { - iconElem.setAttribute("icon", offIcon); - } - } - } - } - } - } - tabContainerMargin(tabContainer); -} - -function templates(template, tabs, _hass, header) { - // Variables for templates. - let states = _hass.states; - let entity = states; - - const templateEval = template => { + templateEval(template, states) { + let entity = states; try { if (template.includes("return")) { return eval(`(function() {${template}}())`); @@ -1000,326 +1479,384 @@ function templates(template, tabs, _hass, header) { return eval(template); } } catch (e) { - console.log("CCH styling template failed."); - console.log(e); - } - }; - - for (const condition in template) { - if (condition == "tab") { - for (const tab in template[condition]) { - let tempCond = template[condition][tab]; - if (!tempCond.length) tempCond = [tempCond]; - tempCond.forEach(templateObj => { - let tabIndex = Object.keys(template[condition]); - let views = lovelace.config.views; - if (isNaN(tabIndex)) { - for (let view in views) { - if ( - views[view]["title"] == tabIndex || - views[view]["path"] == tabIndex - ) { - tabIndex = view; - } - } - } else { - tabIndex = parseInt(tabIndex); - } - let styleTarget = Object.keys(templateObj); - let tabTemplate = templateObj[styleTarget]; - let tabElement = tabs[tabIndex]; - if (styleTarget == "icon") { - tabElement - .querySelector("ha-icon") - .setAttribute("icon", templateEval(tabTemplate, entity)); - } else if (styleTarget == "color") { - tabElement.style.color = templateEval(tabTemplate, entity); - } else if (styleTarget == "display") { - templateEval(tabTemplate, entity) == "show" - ? (tabElement.style.display = "") - : (tabElement.style.display = "none"); - } - }); - } - } else if (condition == "button") { - for (const button in template[condition]) { - let tempCond = template[condition][button]; - if (!tempCond.length) tempCond = [tempCond]; - tempCond.forEach(templateObj => { - let buttonName = Object.keys(template[condition]); - if (newSidebar && buttonName == "notifications") return; - let styleTarget = Object.keys(templateObj); - let buttonElem = buttons[buttonName]; - let tempCond = templateObj[styleTarget]; - let iconTarget = buttonElem.querySelector("paper-icon-button") - ? buttonElem.querySelector("paper-icon-button") - : buttonElem.shadowRoot.querySelector("paper-icon-button"); - if (styleTarget == "icon") { - iconTarget.setAttribute("icon", templateEval(tempCond, entity)); - } else if (styleTarget == "color") { - iconTarget.shadowRoot.querySelector( - "iron-icon" - ).style.color = templateEval(tempCond, entity); - } else if (styleTarget == "display") { - templateEval(tempCond, entity) == "show" - ? (buttonElem.style.display = "") - : (buttonElem.style.display = "none"); - } - }); - } - } else if (condition == "background") { - header.style.background = templateEval(template[condition], entity); + console.log( + `%cCCH Template Failed:%c\n${template}\n%c${e}`, + "text-decoration: underline;", + "", + "color: red;" + ); } } -} -// Get range (e.g., "5 to 9") and build (5,6,7,8,9). -function buildRanges(array) { - let ranges = []; - if (!array) return []; - const sortNumber = (a, b) => a - b; - const range = (start, end) => - new Array(end - start + 1).fill(undefined).map((_, i) => i + start); - for (let i in array) { - if (typeof array[i] == "string" && array[i].includes("to")) { - let split = array[i].split("to"); - if (parseInt(split[1]) > parseInt(split[0])) { - ranges.push(range(parseInt(split[0]), parseInt(split[1]))); - } else { - ranges.push(range(parseInt(split[1]), parseInt(split[0]))); - } - } else if (isNaN(array[i])) { - let views = lovelace.config.views; - for (let view in views) { - if ( - views[view]["title"] == array[i] || - views[view]["path"] == array[i] - ) { - ranges.push(parseInt(view)); + 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"; + let skip_tabs = this.cchConfig.swipe_skip + ? this.buildRanges(this.cchConfig.swipe_skip.split(",")) + : []; + let wrap = + this.cchConfig.swipe_wrap != undefined ? this.cchConfig.swipe_wrap : true; + let prevent_default = + this.cchConfig.swipe_prevent_default != undefined + ? this.cchConfig.swipe_prevent_default + : false; + + swipe_amount /= 10 ** 2; + const appLayout = this.root.querySelector("ha-app-layout"); + let inGroup = true; + let xDown; + let yDown; + let xDiff; + let yDiff; + let activeTab; + let firstTab; + let lastTab; + let left; + let fTabs; + + appLayout.addEventListener("touchstart", handleTouchStart.bind(this), { + passive: true + }); + appLayout.addEventListener("touchmove", handleTouchMove, { + passive: false + }); + appLayout.addEventListener("touchend", handleTouchEnd, { passive: true }); + + click = click.bind(this); + clearClassNames = clearClassNames.bind(this); + animation = animation.bind(this); + + if (!this.root.querySelector("#cch_swipe_animation")) { + let swipeAnimations = document.createElement("style"); + swipeAnimations.setAttribute("id", "cch_swipe_animation"); + swipeAnimations.innerHTML = ` + @keyframes swipeOutRight, swipeOutLeft { + 0% { transform: translateX(0px); opacity: 1; } + } + @keyframes swipeOutRight { + 100% { transform: translateX(${screen.width / 1.5}px); opacity: 0; } + } + @keyframes swipeOutLeft { + 100% { transform: translateX(-${screen.width / 1.5}px); opacity: 0; } + } + @keyframes swipeInRight, swipeInLeft { + 100% { transform: translateX(0px); opacity: 1; } + } + @keyframes swipeInRight { + 0% { transform: translateX(${screen.width / 1.5}px); opacity: 0; } + } + @keyframes swipeInLeft { + 0% { transform: translateX(-${screen.width / 1.5}px); opacity: 0; } + } + @keyframes fadeOut { + 0% { opacity: 1; } + 100% { opacity: 0; } + } + @keyframes fadeIn { + 0% { opacity: 0; } + 100% { opacity: 1; } + } + @keyframes flipOut { + 0% { transform: rotatey(0deg); opacity: 1; } + 100% { transform: rotatey(90deg); opacity: 0; } + } + @keyframes flipIn{ + 0% { transform: rotatey(90deg); opacity: 0; } + 100% { transform: rotatey(0deg); opacity: 1; } + } + .swipeOutRight { animation: swipeOutRight .20s 1; } + .swipeOutLeft { animation: swipeOutLeft .20s 1; } + .swipeInRight { animation: swipeInRight .20s 1; } + .swipeInLeft { animation: swipeInLeft .20s 1; } + .fadeIn { animation: fadeIn .20s 1; } + .fadeOut { animation: fadeOut .20s 1; } + .flipIn { animation: flipIn .20s 1; } + .flipOut { animation: flipOut .20s 1; } + .swipeOutRight, + .swipeOutLeft, + .swipeInRight, + .swipeInLeft, + .fadeIn, + .fadeOut, + .flipIn, + .flipOut { + animation-fill-mode: forwards; + } + `; + this.view.parentNode.appendChild(swipeAnimations); + } + + function handleTouchStart(event) { + filterTabs(this.cchConfig); + if (swipe_groups && !inGroup) return; + let ignored = [ + "APP-HEADER", + "HA-SLIDER", + "SWIPE-CARD", + "HUI-MAP-CARD", + "ROUND-SLIDER", + "HUI-THERMOSTAT-CARD" + ]; + let path = (event.composedPath && event.composedPath()) || event.path; + if (path) { + for (let element of path) { + if (element.nodeName == "HUI-VIEW") break; + else if (ignored.includes(element.nodeName)) return; } } - } else { - ranges.push(parseInt(array[i])); + xDown = event.touches[0].clientX; + yDown = event.touches[0].clientY; } - } - return ranges.flat().sort(sortNumber); -} -function showEditor() { - window.scrollTo(0, 0); - if (!root.querySelector("ha-app-layout").querySelector("editor")) { - const container = document.createElement("editor"); - const nest = document.createElement("div"); - nest.style.cssText = ` - padding: 20px; - max-width: 600px; - margin: 15px auto; - background: var(--paper-card-background-color); - border: 6px solid var(--paper-card-background-color); - `; - container.style.cssText = ` - width: 100%; - min-height: 100%; - box-sizing: border-box; - position: absolute; - background: var(--background-color, grey); - z-index: 2; - padding: 5px; - `; - root.querySelector("ha-app-layout").insertBefore(container, view); - container.appendChild(nest); - nest.appendChild(document.createElement("compact-custom-header-editor")); - } -} - -function reverseObject(object) { - let newObject = {}; - let keys = []; - for (let key in object) keys.push(key); - for (let i = keys.length - 1; i >= 0; i--) { - let value = object[keys[i]]; - newObject[keys[i]] = value; - } - return newObject; -} - -function swipeNavigation(tabs, tabContainer) { - // To make it easier to update lovelace-swipe-navigation - // keep this as close to the standalone lovelace addon as possible. - let swipe_amount = cchConfig.swipe_amount || 15; - let animate = cchConfig.swipe_animate || "none"; - let skip_tabs = cchConfig.swipe_skip - ? buildRanges(cchConfig.swipe_skip.split(",")) - : []; - let wrap = cchConfig.swipe_wrap != undefined ? cchConfig.swipe_wrap : true; - let prevent_default = - cchConfig.swipe_prevent_default != undefined - ? cchConfig.swipe_prevent_default - : false; - - swipe_amount /= Math.pow(10, 2); - const appLayout = root.querySelector("ha-app-layout"); - let xDown, yDown, xDiff, yDiff, activeTab, firstTab, lastTab, left; - - appLayout.addEventListener("touchstart", handleTouchStart, { passive: true }); - appLayout.addEventListener("touchmove", handleTouchMove, { passive: false }); - appLayout.addEventListener("touchend", handleTouchEnd, { passive: true }); - - function handleTouchStart(event) { - let ignored = ["APP-HEADER", "HA-SLIDER", "SWIPE-CARD", "HUI-MAP-CARD"]; - let path = (event.composedPath && event.composedPath()) || event.path; - if (path) { - for (let element of path) { - if (element.nodeName == "HUI-VIEW") break; - else if (ignored.indexOf(element.nodeName) > -1) return; + function handleTouchMove(event) { + if (xDown && yDown) { + xDiff = xDown - event.touches[0].clientX; + yDiff = yDown - event.touches[0].clientY; + if (Math.abs(xDiff) > Math.abs(yDiff) && prevent_default) { + event.preventDefault(); + } } } - xDown = event.touches[0].clientX; - yDown = event.touches[0].clientY; - if (!lastTab) filterTabs(); - activeTab = tabs.indexOf(tabContainer.querySelector(".iron-selected")); - } - function handleTouchMove(event) { - if (xDown && yDown) { - xDiff = xDown - event.touches[0].clientX; - yDiff = yDown - event.touches[0].clientY; - if (Math.abs(xDiff) > Math.abs(yDiff) && prevent_default) { - event.preventDefault(); + function handleTouchEnd() { + if (activeTab < 0 || Math.abs(xDiff) < Math.abs(yDiff)) { + xDown = yDown = xDiff = yDiff = null; + return; + } + if (xDiff > Math.abs(screen.width * swipe_amount)) { + left = false; + if (!wrap && fTabs[activeTab] == lastTab) return; + else if (fTabs[activeTab] == lastTab && wrap) click(firstTab); + else click(fTabs[activeTab + 1]); + } else if (xDiff < -Math.abs(screen.width * swipe_amount)) { + left = true; + if (!wrap && fTabs[activeTab] == firstTab) return; + else if (fTabs[activeTab] == firstTab && wrap) click(lastTab); + else click(fTabs[activeTab - 1]); } - } - } - - function handleTouchEnd() { - if (activeTab < 0 || Math.abs(xDiff) < Math.abs(yDiff)) { xDown = yDown = xDiff = yDiff = null; - return; } - if (xDiff > Math.abs(screen.width * swipe_amount)) { - left = false; - activeTab == tabs.length - 1 ? click(firstTab) : click(activeTab + 1); - } else if (xDiff < -Math.abs(screen.width * swipe_amount)) { - left = true; - activeTab == 0 ? click(lastTab) : click(activeTab - 1); - } - xDown = yDown = xDiff = yDiff = null; - } - function filterTabs() { - tabs = tabs.filter(element => { - return ( - !skip_tabs.includes(tabs.indexOf(element)) && - getComputedStyle(element, null).display != "none" + function filterTabs(config) { + let currentTab = tabs.indexOf( + tabContainer.querySelector(".iron-selected") ); - }); - firstTab = wrap ? 0 : null; - lastTab = wrap ? tabs.length - 1 : null; + if (swipe_groups) { + let groups = swipe_groups.replace(/, /g, ",").split(","); + for (let group in groups) { + let firstLast = groups[group].replace(/ /g, "").split("to"); + if ( + wrap && + currentTab >= firstLast[0] && + currentTab <= firstLast[1] + ) { + inGroup = true; + firstTab = tabs[parseInt(firstLast[0])]; + lastTab = tabs[parseInt(firstLast[1])]; + fTabs = tabs.filter( + element => + tabs.indexOf(element) >= firstLast[0] && + tabs.indexOf(element) <= firstLast[1] + ); + break; + } else { + inGroup = false; + } + } + } + if (config.swipe_skip_hidden) { + fTabs = tabs.filter( + element => + !skip_tabs.includes(tabs.indexOf(element)) && + getComputedStyle(element, null).display != "none" + ); + } else { + fTabs = tabs.filter( + element => !skip_tabs.includes(tabs.indexOf(element)) + ); + } + if (!swipe_groups) { + firstTab = fTabs[0]; + lastTab = fTabs[fTabs.length - 1]; + } + activeTab = fTabs.indexOf(tabContainer.querySelector(".iron-selected")); + } + + function animation(secs, transform, opacity, timeout) { + setTimeout(() => { + this.view.style.transition = `transform ${secs}s, opacity ${secs}s`; + this.view.style.transform = transform ? transform : ""; + this.view.style.opacity = opacity; + }, timeout); + } + + function clearClassNames(huiView) { + [ + "swipeOutRight", + "swipeOutLeft", + "swipeInRight", + "swipeInLeft", + "fadeIn", + "fadeOut", + "flipIn", + "flipOut" + ].forEach(name => { + if (huiView.classList.contains(name)) { + huiView.classList.remove(name); + } + if (this.view.classList.contains(name)) { + this.view.classList.remove(name); + } + }); + huiView.style.overflowX = ""; + this.view.style.overflowX = ""; + } + + function navigate(tab, timeout) { + setTimeout(() => { + tab.dispatchEvent( + new MouseEvent("click", { bubbles: false, cancelable: true }) + ); + }, timeout); + } + + function click(tab) { + if ( + !tab || + this.animation_running || + (tab.style.display == "none" && this.cchConfig.swipe_skip_hidden) + ) { + return; + } + if (animate) + if ( + !wrap && + ((activeTab == firstTab && left) || (activeTab == lastTab && !left)) + ) { + return; + } else if (animate == "swipe") { + const getHuiView = () => { + return ( + this.view.querySelector("hui-view") || + this.view.querySelector("hui-panel-view") + ); + }; + this.animation_running = true; + let huiView = getHuiView(); + clearClassNames(huiView); + huiView.style.overflowX = "hidden"; + this.view.style.overflowX = "hidden"; + // Swipe view off screen and fade out. + huiView.classList.add(left ? "swipeOutRight" : "swipeOutLeft"); + this.view.classList.add("fadeOut"); + setTimeout(() => { + this.view.style.opacity = "0"; + clearClassNames(huiView); + }, 210); + // Watch for destination view to load. + const observer = new MutationObserver(mutations => { + mutations.forEach(({ addedNodes }) => { + addedNodes.forEach(({ nodeName }) => { + if (nodeName) { + // Swipe view on screen and fade in. + huiView = getHuiView(); + huiView.style.overflowX = "hidden"; + this.view.style.overflowX = "hidden"; + this.view.classList.add("fadeIn"); + huiView.classList.add(left ? "swipeInLeft" : "swipeInRight"); + setTimeout(() => { + this.view.style.opacity = "1"; + clearClassNames(huiView); + }, 210); + observer.disconnect(); + return; + } + }); + }); + }); + observer.observe(this.view, { childList: true }); + // Navigate to next view and trigger the observer. + navigate(tab, 220); + } else if (animate == "fade") { + animation(0.16, "", 0, 0); + const observer = new MutationObserver(mutations => { + mutations.forEach(({ addedNodes }) => { + addedNodes.forEach(({ nodeName }) => { + if (nodeName == "HUI-VIEW" || nodeName == "HUI-PANEL-VIEW") { + animation(0.16, "", 1, 0); + observer.disconnect(); + } + }); + }); + }); + observer.observe(this.view, { childList: true }); + navigate(tab, 170); + } else if (animate == "flip") { + animation(0.25, "rotatey(90deg)", 0.25, 0); + const observer = new MutationObserver(mutations => { + mutations.forEach(({ addedNodes }) => { + addedNodes.forEach(({ nodeName }) => { + if (nodeName == "HUI-VIEW" || nodeName == "HUI-PANEL-VIEW") { + animation(0.25, "rotatey(0deg)", 1, 50); + observer.disconnect(); + } + }); + }); + }); + observer.observe(this.view, { childList: true }); + navigate(tab, 270); + } else { + navigate(tab, 0); + } + this.animation_running = false; + } } - function click(index) { + breakingChangeNotification() { if ( - (activeTab == 0 && !wrap && left) || - (activeTab == tabs.length - 1 && !wrap && !left) + this.lovelace.config.cch == undefined && + JSON.stringify(this.lovelace.config.views).includes( + "custom:compact-custom-header" + ) ) { - return; - } - if (animate == "swipe") { - let _in = left ? `${screen.width / 1.5}px` : `-${screen.width / 1.5}px`; - let _out = left ? `-${screen.width / 1.5}px` : `${screen.width / 1.5}px`; - view.style.transitionDuration = "200ms"; - view.style.opacity = 0; - view.style.transform = `translateX(${_in})`; - view.style.transition = "transform 0.20s, opacity 0.20s"; - setTimeout(function() { - tabs[index].dispatchEvent( - new MouseEvent("click", { bubbles: false, cancelable: true }) - ); - view.style.transitionDuration = "0ms"; - view.style.transform = `translateX(${_out})`; - view.style.transition = "transform 0s"; - }, 210); - setTimeout(function() { - view.style.transitionDuration = "200ms"; - view.style.opacity = 1; - view.style.transform = `translateX(0px)`; - view.style.transition = "transform 0.20s, opacity 0.20s"; - }, 215); - } else if (animate == "fade") { - view.style.transitionDuration = "200ms"; - view.style.transition = "opacity 0.20s"; - view.style.opacity = 0; - setTimeout(function() { - tabs[index].dispatchEvent( - new MouseEvent("click", { bubbles: false, cancelable: true }) - ); - view.style.transitionDuration = "0ms"; - view.style.opacity = 0; - view.style.transition = "opacity 0s"; - }, 210); - setTimeout(function() { - view.style.transitionDuration = "200ms"; - view.style.transition = "opacity 0.20s"; - view.style.opacity = 1; - }, 250); - } else if (animate == "flip") { - view.style.transitionDuration = "200ms"; - view.style.transform = "rotatey(90deg)"; - view.style.transition = "transform 0.20s, opacity 0.20s"; - view.style.opacity = 0.25; - setTimeout(function() { - tabs[index].dispatchEvent( - new MouseEvent("click", { bubbles: false, cancelable: true }) - ); - }, 210); - setTimeout(function() { - view.style.transitionDuration = "200ms"; - view.style.transform = "rotatey(0deg)"; - view.style.transition = "transform 0.20s, opacity 0.20s"; - view.style.opacity = 1; - }, 250); - } else { - tabs[index].dispatchEvent( - new MouseEvent("click", { bubbles: false, cancelable: true }) - ); + this.hass.callService("persistent_notification", "create", { + title: "CCH Breaking Change", + notification_id: "CCH_Breaking_Change", + message: + "Compact-Custom-Header's configuration method has changed. You are " + + "receiving this notification because you have updated CCH, but are " + + "using the old config method. Please, visit the [upgrade guide]" + + "(https://maykar.github.io/compact-custom-header/1_1_0_upgrade/) " + + "for more info." + }); } } } -function breakingChangeNotification() { - if ( - lovelace.config.cch == undefined && - JSON.stringify(lovelace.config.views).includes( - "custom:compact-custom-header" - ) - ) { - hass.callService("persistent_notification", "create", { - title: "CCH Breaking Change", - notification_id: "CCH_Breaking_Change", - message: - "Compact-Custom-Header's configuration method has changed. You are " + - "receiving this notification because you have updated CCH, but are " + - "using the old config method. Please, visit the [upgrade guide]" + - "(https://maykar.github.io/compact-custom-header/1_1_0_upgrade/) " + - "for more info." - }); - } -} +const cch = new CompactCustomHeader(); +cch.run(); -// EDITOR ////////////////////////////////////////////////////////////////// - -const buttonOptions = ["show", "hide", "clock", "overflow"]; -const overflowOptions = ["show", "hide", "clock"]; -const swipeAnimation = ["none", "swipe", "fade", "flip"]; -let _lovelace; - -class CompactCustomHeaderEditor extends LitElement { +class CompactCustomHeaderEditor extends cch.LitElement { static get properties() { - return { - _config: {} - }; + return { _config: {} }; } firstUpdated() { + this.html = cch.LitElement.prototype.html; + if ( + !customElements.get("paper-toggle-button") && + customElements.get("ha-switch") + ) { + customElements.define( + "paper-toggle-button", + class extends customElements.get("ha-switch") {} + ); + } + let ll = document.querySelector("home-assistant"); ll = ll && ll.shadowRoot; ll = ll && ll.querySelector("home-assistant-main"); @@ -1328,29 +1865,33 @@ class CompactCustomHeaderEditor extends LitElement { ll = (ll && ll.shadowRoot) || ll; ll = ll && ll.querySelector("ha-panel-lovelace"); ll = ll && ll.shadowRoot; - _lovelace = ll && ll.querySelector("hui-root").lovelace; + this._lovelace = ll && ll.querySelector("hui-root").lovelace; - this._config = _lovelace.config.cch ? deepcopy(_lovelace.config.cch) : {}; + this.deepcopy = this.deepcopy.bind(this); + this._config = this._lovelace.config.cch + ? this.deepcopy(this._lovelace.config.cch) + : {}; } render() { - if (!this._config || !_lovelace) return html``; - return html` + if (!this._config || !this._lovelace) return this.html``; + return this.html`
X
${this.renderStyle()}

Exceptions


- ${this._config.exceptions - ? this._config.exceptions.map((exception, index) => { - return html` + ${ + this._config.exceptions + ? this._config.exceptions.map( + (exception, index) => this.html` - `; - }) - : ""} + ` + ) + : "" + }
- ${this._mwc_button - ? html` + ${ + this._mwc_button + ? this.html` Add Exception ` - : html` + : this.html` Add Exception - `} + ` + }

Current User

-

${hass.user.name}

+

${cch.hass.user.name}

Current User Agent


${navigator.userAgent}

- ${!this.exception - ? html` + ${ + !this.exception + ? this.html` ${this._save_button} ` - : ""} - ${!this.exception - ? html` + : "" + } + ${ + !this.exception + ? this.html` ${this._cancel_button} ` - : ""} + : "" + }

`; } @@ -1410,41 +1958,49 @@ class CompactCustomHeaderEditor extends LitElement { } _save() { - for (var key in this._config) { - if (this._config[key] == defaultConfig[key]) { - delete this._config[key]; + for (const key in this._config) { + if (this._config[key] == cch.defaultConfig[key]) delete this._config[key]; + // Remove old config option. + if (typeof this._config.background == "boolean") { + this._config.background = ""; } } - let newConfig = { - ..._lovelace.config, - ...{ cch: this._config } - }; - try { - _lovelace.saveConfig(newConfig).then(() => { - location.reload(true); - }); - } catch (e) { - alert("Save failed: " + e); + let newConfig = { ...this._lovelace.config, ...{ cch: this._config } }; + 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 - ? html` - Save and Reload + ? this.html` + ${text} ` - : html` + : this.html` Save and Reload${text} `; } get _cancel_button() { return this._mwc_button - ? html` + ? this.html` Cancel ` - : html` + : this.html` Cancel `; } @@ -1453,78 +2009,156 @@ class CompactCustomHeaderEditor extends LitElement { let newExceptions; if (this._config.exceptions) { newExceptions = this._config.exceptions.slice(0); - newExceptions.push({ - conditions: {}, - config: {} - }); + newExceptions.push({ conditions: {}, config: {} }); } else { - newExceptions = [ - { - conditions: {}, - config: {} - } - ]; + newExceptions = [{ conditions: {}, config: {} }]; } - this._config = { - ...this._config, - exceptions: newExceptions - }; + this._config = { ...this._config, exceptions: newExceptions }; - fireEvent(this, "config-changed", { - config: this._config - }); + cch.fireEvent(this, "config-changed", { config: this._config }); } - _configChanged(ev) { - if (!this._config) { - return; - } - this._config = { - ...this._config, - ...ev.detail.config - }; - fireEvent(this, "config-changed", { - config: this._config - }); + _configChanged({ detail }) { + if (!this._config) return; + this._config = { ...this._config, ...detail.config }; + cch.fireEvent(this, "config-changed", { config: this._config }); } _exceptionChanged(ev) { - if (!this._config) { - return; - } + if (!this._config) return; const target = ev.target.index; const newExceptions = this._config.exceptions.slice(0); newExceptions[target] = ev.detail.exception; - this._config = { - ...this._config, - exceptions: newExceptions - }; + this._config = { ...this._config, exceptions: newExceptions }; - fireEvent(this, "config-changed", { - config: this._config - }); + cch.fireEvent(this, "config-changed", { config: this._config }); } _exceptionDelete(ev) { - if (!this._config) { - return; - } + if (!this._config) return; const target = ev.target; const newExceptions = this._config.exceptions.slice(0); newExceptions.splice(target.index, 1); - this._config = { - ...this._config, - exceptions: newExceptions - }; + this._config = { ...this._config, exceptions: newExceptions }; - fireEvent(this, "config-changed", { - config: this._config - }); + cch.fireEvent(this, "config-changed", { config: this._config }); this.requestUpdate(); } + deepcopy(value) { + if (!(!!value && typeof value == "object")) return value; + if (Object.prototype.toString.call(value) == "[object Date]") { + return new Date(value.getTime()); + } + if (Array.isArray(value)) return value.map(this.deepcopy); + const result = {}; + Object.keys(value).forEach(key => { + result[key] = this.deepcopy(value[key]); + }); + 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 html` + return this.html` - ${!this.exception - ? html` + ${ + !this.exception + ? this.html`

- Compact Custom Header + Compact Custom Header  ₁.₄.₉

- ${this.getConfig("warning") - ? html` + ${ + this.getConfig("warning") + ? this.html`
Modifying options marked with a @@ -1671,15 +2314,19 @@ class CchConfigEditor extends LitElement {

` - : ""} + : "" + } ` - : ""} + : "" + } ${this.renderStyle()}
Kiosk Mode - ${this.getConfig("warning") - ? html` + ${ + this.getConfig("warning") + ? this.html` ` - : ""} + : "" + } Display Header - ${this.getConfig("warning") - ? html` + ${ + this.getConfig("warning") + ? this.html` ` - : ""} + : "" + }
`; } _toggleCard() { this._closed = !this._closed; - fireEvent(this, "iron-resize"); + cch.fireEvent(this, "iron-resize"); } _tabVisibility() { - let show = this.shadowRoot.querySelector('[id="show"]'); - let hide = this.shadowRoot.querySelector('[id="hide"]'); - if (this.shadowRoot.querySelector('[id="tabs"]').value == "Hide Tabs") { + let show = this.shadowRoot.querySelector("#show"); + let hide = this.shadowRoot.querySelector("#hide"); + if (this.shadowRoot.querySelector("#tabs").value == "Hide Tabs") { show.style.display = "none"; hide.style.display = "initial"; } else { @@ -2186,31 +2886,25 @@ class CchConfigEditor extends LitElement { } _valueChanged(ev) { - if (!this.config) { - return; - } - const target = ev.target; - if (this[`_${target.configValue}`] === target.value) { - return; - } - if (target.configValue) { - if (target.value === "") { - delete this.config[target.configValue]; + if (!this.config) return; + if (ev.target.configValue) { + if (ev.target.value === "") { + delete this.config[ev.target.configValue]; } else { this.config = { ...this.config, - [target.configValue]: - target.checked !== undefined ? target.checked : target.value + [ev.target.configValue]: + ev.target.checked !== undefined + ? ev.target.checked + : ev.target.value }; } } - fireEvent(this, "cch-config-changed", { - config: this.config - }); + cch.fireEvent(this, "cch-config-changed", { config: this.config }); } renderStyle() { - return html` + return this.html` - - -
- ${title} -
-
- `; - popup.appendChild(message); - this.moreInfo(Object.keys(this.hass.states)[0]); - let moreInfo = document.querySelector("home-assistant")._moreInfoEl; - moreInfo._page = "none"; - moreInfo.shadowRoot.appendChild(popup); - moreInfo.large = large; - document.querySelector("home-assistant").provideHass(message); - - setTimeout(() => { - let interval = setInterval(() => { - if (moreInfo.getAttribute('aria-hidden')) { - popup.parentNode.removeChild(popup); - clearInterval(interval); - } - }, 100) - }, 1000); - return moreInfo; - } - static closePopUp() { - let moreInfo = document.querySelector("home-assistant")._moreInfoEl; - if (moreInfo) moreInfo.close() - } - - static logger(message, script=null) { - if(!('debug' in this.args(script))) return; - - if(typeof message !== "string") - message = JSON.stringify(message); - console.log(`%cDEBUG:%c ${message}`, - "color: blue; font-weight: bold", ""); - } - -}); - -// Global definition of cardTools -var cardTools = customElements.get('card-tools'); - -console.info(`%cCARD-TOOLS IS INSTALLED -%cDeviceID: ${customElements.get('card-tools').deviceID}`, -"color: green; font-weight: bold", -""); +!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=3)}([function(e,t,r){"use strict";function n(){return document.querySelector("home-assistant").hass}function o(e){return document.querySelector("home-assistant").provideHass(e)}function s(){var e=document.querySelector("home-assistant");if(e=(e=(e=(e=(e=(e=(e=(e=e&&e.shadowRoot)&&e.querySelector("home-assistant-main"))&&e.shadowRoot)&&e.querySelector("app-drawer-layout partial-panel-resolver"))&&e.shadowRoot||e)&&e.querySelector("ha-panel-lovelace"))&&e.shadowRoot)&&e.querySelector("hui-root")){var t=e.lovelace;return t.current_view=e.___curView,t}return null}function a(){var e=document.querySelector("home-assistant");return e=(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=e&&e.shadowRoot)&&e.querySelector("home-assistant-main"))&&e.shadowRoot)&&e.querySelector("app-drawer-layout partial-panel-resolver"))&&e.shadowRoot||e)&&e.querySelector("ha-panel-lovelace"))&&e.shadowRoot)&&e.querySelector("hui-root"))&&e.shadowRoot)&&e.querySelector("ha-app-layout #view"))&&e.firstElementChild}r.d(t,"a",(function(){return n})),r.d(t,"d",(function(){return o})),r.d(t,"b",(function(){return s})),r.d(t,"c",(function(){return a}))},function(e,t,r){"use strict";r.d(t,"a",(function(){return n}));let n=function(){if(window.fully&&"function"==typeof fully.getDeviceId)return fully.getDeviceId();if(!localStorage["lovelace-player-device-id"]){const e=()=>Math.floor(1e5*(1+Math.random())).toString(16).substring(1);localStorage["lovelace-player-device-id"]=`${e()}${e()}-${e()}${e()}`}return localStorage["lovelace-player-device-id"]}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.d(__webpack_exports__,"a",(function(){return hasOldTemplate})),__webpack_require__.d(__webpack_exports__,"b",(function(){return parseOldTemplate}));var _hass_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0),_deviceID_js__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(1);function hasOldTemplate(e){return/\[\[\s+.*\s+\]\]/.test(e)}function parseTemplateString(str,specialData={}){if("string"!=typeof str)return text;const FUNCTION=/^[a-zA-Z0-9_]+\(.*\)$/,EXPR=/([^=<>!]+)\s*(==|!=|<|>|<=|>=)\s*([^=<>!]+)/,SPECIAL=/^\{.+\}$/,STRING=/^"[^"]*"|'[^']*'$/;"string"==typeof specialData&&(specialData={}),specialData=Object.assign({user:Object(_hass_js__WEBPACK_IMPORTED_MODULE_0__.a)().user.name,browser:_deviceID_js__WEBPACK_IMPORTED_MODULE_1__.a,hash:location.hash.substr(1)||" "},specialData);const _parse_function=e=>{let t=[e.substr(0,e.indexOf("(")).trim()];for(e=e.substr(e.indexOf("(")+1);e;){let r=0,n=0,o=!1;for(;e[r];){let t=e[r++];if(t===o&&r>1&&"\\"!==e[r-2]?o=!1:"\"'".includes(t)&&(o=t),!o){if("("===t)n+=1;else if(")"===t){n-=1;continue}if(!(n>0)&&",)".includes(t))break}}t.push(e.substr(0,r-1).trim()),e=e.substr(r)}return t},_parse_special=e=>(e=e.substr(1,e.length-2),specialData[e]||`{${e}}`),_parse_entity=e=>{let t;if((e=e.split("."))[0].match(SPECIAL))t=_parse_special(e.shift()),t=Object(_hass_js__WEBPACK_IMPORTED_MODULE_0__.a)().states[t]||t;else if(t=Object(_hass_js__WEBPACK_IMPORTED_MODULE_0__.a)().states[`${e.shift()}.${e.shift()}`],!e.length)return t.state;return e.forEach(e=>t=t[e]),t},_eval_expr=str=>{if(str=EXPR.exec(str),null===str)return!1;const lhs=parseTemplateString(str[1]),rhs=parseTemplateString(str[3]);var expr="";return expr=parseFloat(lhs)!=lhs?`"${lhs}" ${str[2]} "${rhs}"`:`${parseFloat(lhs)} ${str[2]} ${parseFloat(rhs)}`,eval(expr)},_eval_function=e=>{if("if"===e[0])return _eval_expr(e[1])?parseTemplateString(e[2]):parseTemplateString(e[3])};try{return str=str.trim(),str.match(STRING)?str.substr(1,str.length-2):str.match(SPECIAL)?_parse_special(str):str.match(FUNCTION)?_eval_function(_parse_function(str)):str.includes(".")?_parse_entity(str):str}catch(e){return`[[ Template matching failed: ${str} ]]`}}function parseOldTemplate(e,t={}){if("string"!=typeof e)return e;return e=e.replace(/\[\[\s(.*?)\s\]\]/g,(e,r,n,o)=>parseTemplateString(r,t))}},function(e,t,r){"use strict";r.r(t);const n=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),o=n.prototype.html,s=n.prototype.css;function a(e,t,r=null){if((e=new Event(e,{bubbles:!0,cancelable:!1,composed:!0})).detail=t||{},r)r.dispatchEvent(e);else{var n=document.querySelector("home-assistant");(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=n&&n.shadowRoot)&&n.querySelector("home-assistant-main"))&&n.shadowRoot)&&n.querySelector("app-drawer-layout partial-panel-resolver"))&&n.shadowRoot||n)&&n.querySelector("ha-panel-lovelace"))&&n.shadowRoot)&&n.querySelector("hui-root"))&&n.shadowRoot)&&n.querySelector("ha-app-layout #view"))&&n.firstElementChild)&&n.dispatchEvent(e)}}const i="custom:";function c(e,t){const r=document.createElement("hui-error-card");return r.setConfig({type:"error",error:e,origConfig:t}),r}function l(e,t){if(!t||"object"!=typeof t||!t.type)return c(`No ${e} type configured`,t);let r=t.type;if(r=r.startsWith(i)?r.substr(i.length):`hui-${r}-${e}`,customElements.get(r))return function(e,t){const r=document.createElement(e);try{r.setConfig(t)}catch(e){return c(e,t)}return r}(r,t);const n=c(`Custom element doesn't exist: ${r}.`,t);n.style.display="None";const o=setTimeout(()=>{n.style.display=""},2e3);return customElements.whenDefined(r).then(()=>{clearTimeout(o),a("ll-rebuild",{},n)}),n}function u(e){return l("card",e)}function p(e){return l("element",e)}function d(e){const t=new Set(["call-service","divider","section","weblink"]);if(!e)return c("Invalid configuration given.",e);if("string"==typeof e&&(e={entity:e}),"object"!=typeof e||!e.entity&&!e.type)return c("Invalid configuration given.",e);const r=e.type||"default";if(t.has(r)||r.startsWith(i))return l("row",e);const n=e.entity.split(".",1)[0];return Object.assign(e,{type:{alert:"toggle",automation:"toggle",climate:"climate",cover:"cover",fan:"toggle",group:"group",input_boolean:"toggle",input_number:"input-number",input_select:"input-select",input_text:"input-text",light:"toggle",lock:"lock",media_player:"media-player",remote:"toggle",scene:"scene",script:"script",sensor:"sensor",timer:"timer",switch:"toggle",vacuum:"toggle",water_heater:"climate",input_datetime:"input-datetime"}[n]||"text"}),l("entity-row",e)}var f=r(0);const m=2;class h extends n{static get version(){return m}static get properties(){return{noHass:{type:Boolean}}}setConfig(e){this._config=e,this.el?this.el.setConfig(e):(this.el=this.create(e),this._hass&&(this.el.hass=this._hass),this.noHass&&Object(f.d)(this))}set config(e){this.setConfig(e)}set hass(e){this._hass=e,this.el&&(this.el.hass=e)}createRenderRoot(){return this}render(){return o`${this.el}`}}const _=function(e,t){const r=Object.getOwnPropertyDescriptors(t.prototype);for(const[t,n]of Object.entries(r))"constructor"!==t&&Object.defineProperty(e.prototype,t,n);const n=Object.getOwnPropertyDescriptors(t);for(const[t,r]of Object.entries(n))"prototype"!==t&&Object.defineProperty(e,t,r);const o=Object.getPrototypeOf(t),s=Object.getOwnPropertyDescriptors(o.prototype);for(const[t,r]of Object.entries(s))"constructor"!==t&&Object.defineProperty(Object.getPrototypeOf(e).prototype,t,r);const a=Object.getOwnPropertyDescriptors(o);for(const[t,r]of Object.entries(a))"prototype"!==t&&Object.defineProperty(Object.getPrototypeOf(e),t,r)},g=customElements.get("card-maker");if(!g||!g.version||g.version{document.body.querySelector("long-press").bind(e)}),customElements.whenDefined("action-handler").then(()=>{document.body.querySelector("action-handler").bind(e,t)}),e}function O(e,t=!1){a("hass-more-info",{entityId:e},document.querySelector("home-assistant"));const r=document.querySelector("home-assistant")._moreInfoEl;return r.large=t,r}function E(){const e=document.querySelector("home-assistant")&&document.querySelector("home-assistant")._moreInfoEl;e&&e.close()}function S(e,t,r=!1,n=null,o=!1){a("hass-more-info",{entityId:null});const s=document.querySelector("home-assistant")._moreInfoEl;s.close(),s.open();const i=document.createElement("div");i.innerHTML=`\n \n ${o?"":`\n \n \n
\n ${e}\n
\n
\n `}\n
\n \n \n
\n `;const c=i.querySelector(".scrollable");c.querySelector("card-maker").config=t,s.sizingTarget=c,s.large=r,s._page="none",s.shadowRoot.appendChild(i);let l={};if(n)for(var u in s.resetFit(),n)l[u]=s.style[u],s.style.setProperty(u,n[u]);return s._dialogOpenChanged=function(e){if(!e&&(this.stateObj&&this.fire("hass-more-info",{entityId:null}),this.shadowRoot==i.parentNode&&(this._page=null,this.shadowRoot.removeChild(i),n)))for(var t in s.resetFit(),l)l[t]?s.style.setProperty(t,l[t]):s.style.removeProperty(t)},s}function j(e,t,r){e||(e=Object(f.a)().connection);let n={user:Object(f.a)().user.name,browser:v.a,hash:location.hash.substr(1)||" ",...r.variables},o=r.template,s=r.entity_ids;return e.subscribeMessage(e=>t(e.result),{type:"render_template",template:o,variables:n,entity_ids:s})}var D=r(2);const T=Object(f.a)().callWS({type:"config/area_registry/list"}),P=Object(f.a)().callWS({type:"config/device_registry/list"}),C=Object(f.a)().callWS({type:"config/entity_registry/list"});async function q(){return window.cardToolsData=window.cardToolsData||{areas:await T,devices:await P,entities:await C},window.cardToolsData}function R(e){const t=window.cardToolsData;for(const r of t.areas)if(r.name.toLowerCase()===e.toLowerCase())return r;return null}function I(e){const t=window.cardToolsData;let r=[];if(!e)return r;for(const n of t.devices)n.area_id===e.area_id&&r.push(n);return r}function x(e){const t=window.cardToolsData;for(const r of t.devices)if(r.name.toLowerCase()===e.toLowerCase())return r;return null}function $(e){const t=window.cardToolsData;let r=[];if(!e)return r;for(const n of t.entities)n.device_id===e.id&&r.push(n.entity_id);return r}q();class k{static checkVersion(e){}static args(){}static logger(){}static get localize(){return Object(f.a)().localize}static get deviceID(){return v.a}static get fireEvent(){return a}static get hass(){return Object(f.a)()}static get lovelace(){return Object(f.b)()}static get lovelace_view(){return f.c}static get provideHass(){return f.d}static get LitElement(){return n}static get LitHtml(){return o}static get LitCSS(){return s}static get longpress(){return w}static get createCard(){return u}static get createElement(){return p}static get createEntityRow(){return d}static get moreInfo(){return O}static get popUp(){return S}static get closePopUp(){return E}static get hasTemplate(){return D.a}static parseTemplate(e,t,r={}){return"string"==typeof e?Object(D.b)(e,t):async function(e,t,r={}){for(var n in e||(e=e()),r={},r=Object.assign({user:e.user.name,browser:v.a,hash:location.hash.substr(1)||" "},r)){var o=new RegExp(`\\{${n}\\}`,"g");t=t.replace(o,r[n])}return e.callApi("POST","template",{template:t})}(e,t,r)}static get subscribeRenderTemplate(){return j}static get getData(){return q}static get areaByName(){return R}static get areaDevices(){return I}static get deviceByName(){return x}static get deviceEntities(){return $}}customElements.get("card-tools")||(customElements.define("card-tools",k),window.cardTools=customElements.get("card-tools"),console.info(`%cCARD-TOOLS 2 IS INSTALLED\n %cDeviceID: ${customElements.get("card-tools").deviceID}`,"color: green; font-weight: bold",""))}]); \ No newline at end of file diff --git a/www/community/lovelace-card-tools/card-tools.js.gz b/www/community/lovelace-card-tools/card-tools.js.gz index 7cbadc1ce4b70c3dfd12bd55c24c0aae2b4aefd3..315ed2d482a47597575381546038a81816559f80 100644 GIT binary patch literal 4477 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 Ph`s*_Ro3+XBQyX2HP@!H literal 4107 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 bSSao|tg8=vDq|xKbu8)!p84V{wg3PCeZ@-+ literal 0 HcmV?d00001 diff --git a/www/community/lovelace-fold-entity-row/fold-entity-row.js b/www/community/lovelace-fold-entity-row/fold-entity-row.js index 447a16a..040a64d 100644 --- a/www/community/lovelace-fold-entity-row/fold-entity-row.js +++ b/www/community/lovelace-fold-entity-row/fold-entity-row.js @@ -1,8 +1,8 @@ -!function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);const i=Object.getPrototypeOf(customElements.get("home-assistant-main")),o=i.prototype.html,s=i.prototype.css;function r(e,t,n=null){if((e=new Event(e,{bubbles:!0,cancelable:!1,composed:!0})).detail=t||{},n)n.dispatchEvent(e);else{var i=document.querySelector("home-assistant");(i=(i=(i=(i=(i=(i=(i=(i=(i=(i=(i=i&&i.shadowRoot)&&i.querySelector("home-assistant-main"))&&i.shadowRoot)&&i.querySelector("app-drawer-layout partial-panel-resolver"))&&i.shadowRoot||i)&&i.querySelector("ha-panel-lovelace"))&&i.shadowRoot)&&i.querySelector("hui-root"))&&i.shadowRoot)&&i.querySelector("ha-app-layout #view"))&&i.firstElementChild)&&i.dispatchEvent(e)}}const a="custom:",c=["input_number","input_select","input_text","input_datetime","scene","weblink"];function l(e,t){const n=document.createElement("hui-error-card");return n.setConfig({type:"error",error:e,config:t}),n}function u(e,t){if(!t||"object"!=typeof t||!t.type)return l(`No ${e} type configured`,t);let n=t.type;if(n=n.startsWith(a)?n.substr(a.length):`hui-${n}-${e}`,customElements.get(n))return function(e,t){const n=document.createElement(e);try{n.setConfig(t)}catch(e){return l(e,t)}return n}(n,t);const i=l(`Custom element doesn't exist: ${n}.`,t);i.style.display="None";const o=setTimeout(()=>{i.style.display=""},2e3);return customElements.whenDefined(n).then(()=>{clearTimeout(o),r("ll-rebuild",{},i)}),i}class h extends i{static get properties(){return{hass:{},config:{}}}setConfig(e){var t;this._config=e,this.el?this.el.setConfig(e):this.el=this.create(e),this._hass&&(this.el.hass=this._hass),this.noHass&&(t=this,document.querySelector("home-assistant").provideHass(t))}set config(e){this.setConfig(e)}set hass(e){this._hass=e,this.el&&(this.el.hass=e)}createRenderRoot(){return this}render(){return o`${this.el}`}}if(!customElements.get("card-maker")){class e extends h{create(e){return function(e){return u("card",e)}(e)}}customElements.define("card-maker",e)}if(!customElements.get("element-maker")){class e extends h{create(e){return function(e){return u("element",e)}(e)}}customElements.define("element-maker",e)}if(!customElements.get("entity-row-maker")){class e extends h{create(e){return function(e){const t=new Set(["call-service","divider","section","weblink"]);if(!e)return l("Invalid configuration given.",e);if("string"==typeof e&&(e={entity:e}),"object"!=typeof e||!e.entity&&!e.type)return l("Invalid configuration given.",e);const n=e.type||"default";if(t.has(n)||n.startsWith(a))return u("row",e);const i=e.entity.split(".",1)[0];return Object.assign(e,{type:{alert:"toggle",automation:"toggle",climate:"climate",cover:"cover",fan:"toggle",group:"group",input_boolean:"toggle",input_number:"input-number",input_select:"input-select",input_text:"input-text",light:"toggle",lock:"lock",media_player:"media-player",remote:"toggle",scene:"scene",script:"script",sensor:"sensor",timer:"timer",switch:"toggle",vacuum:"toggle",water_heater:"climate",input_datetime:"input-datetime"}[i]||"text"}),u("entity-row",e)}(e)}}customElements.define("entity-row-maker",e)}customElements.define("fold-entity-row",class extends i{static get properties(){return{hass:{},open:Boolean,items:{}}}setConfig(e){this._config=Object.assign({},{open:!1,padding:20,group_config:{}},e),this.open=this.open||this._config.open,this.items=this._config.items,this._config.entities&&(this.items=this._config.entities),"string"==typeof this._config.head&&this._config.head.startsWith("group.")&&(this.items=document.querySelector("home-assistant").hass.states[this._config.head].attributes.entity_id)}clickRow(e){const t=e.target.parentElement._config,n=t.entity||("string"==typeof t?t:null);e.stopPropagation(),this.hasMoreInfo(t)?function(e,t=!1){r("hass-more-info",{entityId:e},document.querySelector("home-assistant"));const n=document.querySelector("home-assistant")._moreInfoEl;n.large=t}(n):e.target.parentElement.hasAttribute("head")&&this.toggle(e)}toggle(e){e&&e.stopPropagation(),this.open=!this.open}hasMoreInfo(e){const t=e.entity||("string"==typeof e?e:null);return!(!t||c.includes(t.split(".",1)[0]))}firstUpdated(){const e=this.shadowRoot.querySelector("#head > entity-row-maker");e.updateComplete.then(()=>{const t=e.querySelector("hui-section-row");t&&t.updateComplete.then(()=>{t.shadowRoot.querySelector(".divider").style.marginRight="-56px"})})}render(){this._entities&&this._entities.forEach(e=>e.hass=this.hass);const e=e=>("string"==typeof e&&(e={entity:e}),Object.assign({},this._config.group_config,e));return o` +!function(t){var e={};function i(n){if(e[n])return e[n].exports;var s=e[n]={i:n,l:!1,exports:{}};return t[n].call(s.exports,s,s.exports,i),s.l=!0,s.exports}i.m=t,i.c=e,i.d=function(t,e,n){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var s in t)i.d(n,s,function(e){return t[e]}.bind(null,s));return n},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=0)}([function(t,e,i){"use strict";i.r(e);const n=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),s=n.prototype.html,o=n.prototype.css;function r(){return document.querySelector("home-assistant").hass}const a="custom:",c=["input_number","input_select","input_text","scene","weblink"];function l(t,e){const i=document.createElement("hui-error-card");return i.setConfig({type:"error",error:t,config:e}),i}function h(t,e){if(!e||"object"!=typeof e||!e.type)return l(`No ${t} type configured`,e);let i=e.type;if(i=i.startsWith(a)?i.substr(a.length):`hui-${i}-${t}`,customElements.get(i))return function(t,e){const i=document.createElement(t);try{i.setConfig(e)}catch(t){return l(t,e)}return i}(i,e);const n=l(`Custom element doesn't exist: ${i}.`,e);n.style.display="None";const s=setTimeout(()=>{n.style.display=""},2e3);return customElements.whenDefined(i).then(()=>{clearTimeout(s),function(t,e,i=null){if((t=new Event(t,{bubbles:!0,cancelable:!1,composed:!0})).detail=e||{},i)i.dispatchEvent(t);else{var n=document.querySelector("home-assistant");(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=(n=n&&n.shadowRoot)&&n.querySelector("home-assistant-main"))&&n.shadowRoot)&&n.querySelector("app-drawer-layout partial-panel-resolver"))&&n.shadowRoot||n)&&n.querySelector("ha-panel-lovelace"))&&n.shadowRoot)&&n.querySelector("hui-root"))&&n.shadowRoot)&&n.querySelector("ha-app-layout #view"))&&n.firstElementChild)&&n.dispatchEvent(t)}}("ll-rebuild",{},n)}),n}class u extends n{static get properties(){return{hass:{},config:{},noHass:{type:Boolean}}}setConfig(t){var e;this._config=t,this.el?this.el.setConfig(t):this.el=this.create(t),this._hass&&(this.el.hass=this._hass),this.noHass&&(e=this,document.querySelector("home-assistant").provideHass(e))}set config(t){this.setConfig(t)}set hass(t){this._hass=t,this.el&&(this.el.hass=t)}createRenderRoot(){return this}render(){return s`${this.el}`}}if(!customElements.get("card-maker")){class t extends u{create(t){return function(t){return h("card",t)}(t)}getCardSize(){return this.firstElementChild&&this.firstElementChild.getCardSize?this.firstElementChild.getCardSize():1}}customElements.define("card-maker",t)}if(!customElements.get("element-maker")){class t extends u{create(t){return function(t){return h("element",t)}(t)}}customElements.define("element-maker",t)}if(!customElements.get("entity-row-maker")){class t extends u{create(t){return function(t){const e=new Set(["call-service","divider","section","weblink"]);if(!t)return l("Invalid configuration given.",t);if("string"==typeof t&&(t={entity:t}),"object"!=typeof t||!t.entity&&!t.type)return l("Invalid configuration given.",t);const i=t.type||"default";if(e.has(i)||i.startsWith(a))return h("row",t);const n=t.entity.split(".",1)[0];return Object.assign(t,{type:{alert:"toggle",automation:"toggle",climate:"climate",cover:"cover",fan:"toggle",group:"group",input_boolean:"toggle",input_number:"input-number",input_select:"input-select",input_text:"input-text",light:"toggle",lock:"lock",media_player:"media-player",remote:"toggle",scene:"scene",script:"script",sensor:"sensor",timer:"timer",switch:"toggle",vacuum:"toggle",water_heater:"climate",input_datetime:"input-datetime"}[n]||"text"}),h("entity-row",t)}(t)}}customElements.define("entity-row-maker",t)}customElements.define("fold-entity-row",class extends n{static get properties(){return{_hass:{},open:Boolean,items:{}}}setConfig(t){this._config=Object.assign({},{open:!1,padding:20,group_config:{}},t),this.open=this.open||this._config.open,this.head=this._config.head,this._config.entity&&(this.head=this._config.entity),"string"==typeof this.head&&(this.head={entity:this.head}),this.items=this._config.items,this._config.entities&&(this.items=this._config.entities),this.head.entity&&this.head.entity.startsWith("group.")&&!this.items&&(this.items=r().states[this.head.entity].attributes.entity_id)}clickRow(t){t.stopPropagation();const e=t.target.parentElement._config;this.hasMoreInfo(e)||e.tap_action?customElements.get("hui-entities-card").prototype._handleClick.bind(this)(e):t.target.parentElement.hasAttribute("head")&&this.toggle(t)}toggle(t){t&&t.stopPropagation(),this.open=!this.open}hasMoreInfo(t){const e=t.entity||("string"==typeof t?t:null);return!(!e||c.includes(e.split(".",1)[0]))}firstUpdated(){const t=this.shadowRoot.querySelector("#head > entity-row-maker");t.updateComplete.then(()=>{const e=t.querySelector("hui-section-row");e&&e.updateComplete.then(()=>{e.shadowRoot.querySelector(".divider").style.marginRight="-56px"})})}set hass(t){this._hass=t}render(){this._entities&&this._entities.forEach(t=>t.hass=this._hass);const t=t=>("string"==typeof t&&(t={entity:t}),Object.assign({},this._config.group_config,t));return s` - `}static get styles(){return s` + `}static get styles(){return o` #head { + --toggle-icon-width: 40px; display: flex; cursor: pointer; align-items: center; } #head entity-row-maker { flex-grow: 1; + max-width: calc(100% - var(--toggle-icon-width)); } #head ha-icon { - width: 40px; + width: var(--toggle-icon-width); cursor: pointer } diff --git a/www/community/lovelace-fold-entity-row/fold-entity-row.js.gz b/www/community/lovelace-fold-entity-row/fold-entity-row.js.gz index ff0b80c68382298b14f029aae982012549898fd1..3caac218f71446cb1d1cbd446144fb618328bba2 100644 GIT binary patch literal 2524 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 { -let cardTools = customElements.get('card-tools'); -class LayoutCard extends cardTools.LitElement { - - async setConfig(config) { - this.config = config; - this.layout = config.layout || 'auto'; - this.minCols = config.column_num || 1; - this.maxCols = config.max_columns || 100; - this.colWidth = config.column_width || 400; - this.maxWidth = config.max_width || 500; - this.minHeight = config.min_height || 5; - this.rtl = config.rtl || false; - this.cardSize = 1; - - this.make_cards(); - - window.addEventListener('resize', () => this.build()); - window.addEventListener('hass-open-menu', () => setTimeout(() => this.build(), 100)); - window.addEventListener('hass-close-menu', () => setTimeout(() => this.build(), 100)); - window.addEventListener('location-changed', () => { - if(location.hash === "") setTimeout(() => - this.build(), 100) - }); - if(config.rebuild) - window.setTimeout(() => this.build(), config.rebuild); - } - - render() { - return cardTools.LitHtml` -
- `; - } - - firstUpdated() { - if(this.parentElement && this.parentElement.id !== "view") - { - this.style.padding = "0"; - } - if(this.rtl) - this.shadowRoot.querySelector("#columns").style.flexDirection = 'row-reverse'; - this.build(); - this._cardModder = { - target: this, - styles: this.shadowRoot.querySelector("style") - }; - } - - static get styles() { - return cardTools.LitCSS` +!function(t){var e={};function n(s){if(e[s])return e[s].exports;var o=e[s]={i:s,l:!1,exports:{}};return t[s].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,s){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:s})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(s,o,function(e){return t[e]}.bind(null,o));return s},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=0)}([function(t,e,n){"use strict";n.r(e);const s=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),o=s.prototype.html,i=s.prototype.css;const r="custom:";function c(t,e){const n=document.createElement("hui-error-card");return n.setConfig({type:"error",error:t,origConfig:e}),n}function a(t,e){if(!e||"object"!=typeof e||!e.type)return c(`No ${t} type configured`,e);let n=e.type;if(n=n.startsWith(r)?n.substr(r.length):`hui-${n}-${t}`,customElements.get(n))return function(t,e){const n=document.createElement(t);try{n.setConfig(e)}catch(t){return c(t,e)}return n}(n,e);const s=c(`Custom element doesn't exist: ${n}.`,e);s.style.display="None";const o=setTimeout(()=>{s.style.display=""},2e3);return customElements.whenDefined(n).then(()=>{clearTimeout(o),function(t,e,n=null){if((t=new Event(t,{bubbles:!0,cancelable:!1,composed:!0})).detail=e||{},n)n.dispatchEvent(t);else{var s=document.querySelector("home-assistant");(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=(s=s&&s.shadowRoot)&&s.querySelector("home-assistant-main"))&&s.shadowRoot)&&s.querySelector("app-drawer-layout partial-panel-resolver"))&&s.shadowRoot||s)&&s.querySelector("ha-panel-lovelace"))&&s.shadowRoot)&&s.querySelector("hui-root"))&&s.shadowRoot)&&s.querySelector("ha-app-layout #view"))&&s.firstElementChild)&&s.dispatchEvent(t)}}("ll-rebuild",{},s)}),s}function l(){return document.querySelector("home-assistant").hass}const u=2;class d extends s{static get version(){return u}static get properties(){return{noHass:{type:Boolean}}}setConfig(t){var e;this._config=t,this.el?this.el.setConfig(t):(this.el=this.create(t),this._hass&&(this.el.hass=this._hass),this.noHass&&(e=this,document.querySelector("home-assistant").provideHass(e)))}set config(t){this.setConfig(t)}set hass(t){this._hass=t,this.el&&(this.el.hass=t)}createRenderRoot(){return this}render(){return o`${this.el}`}}const h=function(t,e){const n=Object.getOwnPropertyDescriptors(e.prototype);for(const[e,s]of Object.entries(n))"constructor"!==e&&Object.defineProperty(t.prototype,e,s);const s=Object.getOwnPropertyDescriptors(e);for(const[e,n]of Object.entries(s))"prototype"!==e&&Object.defineProperty(t,e,n);const o=Object.getPrototypeOf(e),i=Object.getOwnPropertyDescriptors(o.prototype);for(const[e,n]of Object.entries(i))"constructor"!==e&&Object.defineProperty(Object.getPrototypeOf(t).prototype,e,n);const r=Object.getOwnPropertyDescriptors(o);for(const[e,n]of Object.entries(r))"prototype"!==e&&Object.defineProperty(Object.getPrototypeOf(t),e,n)},m=customElements.get("card-maker");if(!m||!m.version||m.version{if(!t)return;const s=e[function(){let t=0;for(let s=0;sthis.place_cards()),window.addEventListener("hass-open-menu",()=>setTimeout(()=>this.place_cards(),100)),window.addEventListener("hass-close-menu",()=>setTimeout(()=>this.place_cards(),100)),window.addEventListener("location-changed",()=>{""===location.hash&&setTimeout(()=>this.place_cards(),100)})}async updated(t){!this.cards.length&&(this._config.entities&&this._config.entities.length||this._config.cards&&this._config.cards.length)&&(this.cards=await this.build_cards(),this.place_cards()),t.has("hass")&&this.hass&&this.cards&&this.cards.forEach(t=>{t&&(t.hass=this.hass)})}async build_card(t){if("break"===t)return null;const e=document.createElement("card-maker");return e.config={...t,...this._config.card_options},e.hass=l(),this.shadowRoot.querySelector("#staging").appendChild(e),new Promise((t,n)=>e.updateComplete.then(()=>t(e)))}async build_cards(){const t=this.shadowRoot.querySelector("#staging");for(;t.lastChild;)t.removeChild(t.lastChild);return Promise.all((this._config.entities||this._config.cards).map(t=>this.build_card(t)))}place_cards(){const t=this.shadowRoot.querySelector("#columns").clientWidth;this.columns=function(t,e,n){const s=t=>"string"==typeof t&&t.endsWith("%")?Math.floor(e*parseInt(t)/100):parseInt(t);let o=0;if("object"==typeof n.column_width){let t=e;for(;t>0;){let e=n.column_width[o];void 0===e&&(e=n.column_width.slice(-1)[0]),t-=s(e),o+=1}o=Math.max(o-1,1)}else o=Math.floor(e/s(n.column_width));o=Math.max(o,n.min_columns),o=Math.min(o,n.max_columns);let i=[];for(let t=0;t{if(s+=1,!t)return;const n=e[(s-1)%e.length];n.appendChild(t),n.length+=t.getCardSize?t.getCardSize():1})}(t,i);break;case"vertical":!function(t,e,n){let s=0;t.forEach(t=>{if(!t)return void(s+=1);const n=e[s%e.length];n.appendChild(t),n.length+=t.getCardSize?t.getCardSize():1})}(t,i);break;case"auto":default:g(t,i,n)}return i=i.filter(t=>t.childElementCount>0)}(this.cards,t,this._config),this._config.rtl&&this.columns.reverse(),this.format_columns(),this.requestUpdate()}format_columns(){const t=(t,e,n,s="px")=>{if(void 0===this._config[e])return"";let o=`${t}: `;const i=this._config[e];return"object"==typeof i?i.length>n?o+=`${i[n]}`:o+=`${i.slice(-1)}`:o+=`${i}`,o.endsWith("px")||o.endsWith("%")||(o+=s),o+";"};for(const[e,n]of this.columns.entries()){const s=[t("max-width","max_width",e),t("min-width","min_width",e),t("width","column_width",e),t("flex-grow","flex_grow",e,"")];n.style.cssText="".concat(...s)}}getCardSize(){if(this.columns)return Math.max.apply(Math,this.columns.map(t=>t.length))}_isPanel(){if(this.isPanel)return!0;let t=this.parentElement,e=10;for(;e--;){if("hui-panel-view"===t.localName)return!0;if("div"===t.localName)return!1;t=t.parentElement}return!1}render(){return o` +
+ ${this.columns.map(t=>o` + ${t} + `)} +
+
+ `}static get styles(){return i` :host { - padding: 8px 4px 0; + padding: 0 4px; display: block; + margin-bottom: 0!important; } #columns { display: flex; flex-direction: row; justify-content: center; + margin-top: -8px; + } + #columns.panel { + margin-top: 0; } .column { @@ -65,130 +34,19 @@ class LayoutCard extends cardTools.LitElement { overflow-x: hidden; } - .column > * { + card-maker>* { display: block; margin: 4px 4px 8px; } - - .column > *:first-child { - margin-top: 0; + card-maker:first-child>* { + margin-top: 8px; } - `; - } - - make_cards() { - this._cards = this.config.cards.map((c) => { - if (typeof c === 'string') return c; - const card = cardTools.createCard(c); - if(this._hass) card.hass = this._hass; - this.appendChild(card); // Place card in DOM to get size - return card; - }); - } - - update_columns() { - const width = (this.shadowRoot && this.shadowRoot.querySelector("#columns").clientWidth) || (this.parentElement && this.parentElement.clientWidth); - this.colNum = Math.floor(width / this.colWidth) || 1; - this.colNum = Math.max(this.colNum, this.minCols); - this.colNum = Math.min(this.colNum, this.maxCols); - } - - build() { - if (this.offsetParent === null) return; - const root = this.shadowRoot.querySelector("#columns"); - while(root.lastChild) { - root.removeChild(root.lastChild); - } - - this.update_columns(); - - if(!this._cards) this.make_cards(); - - let cols = []; - let colSize = []; - for(let i = 0; i < this.colNum; i++) { - cols.push([]); - colSize.push(0); - } - - const shortestCol = () => { - let i = 0; - for(let j = 0; j < this.colNum; j++) { - if(colSize[j] < this.min_height) - return j; - if(colSize[j] < colSize[i]) - i = j; + card-maker:last-child>* { + margin-bottom: 4px; } - return i; - } - let i = 0; - this._cards.forEach((c) => { - const isBreak = (typeof(c) === 'string'); - const sz = c.getCardSize ? c.getCardSize() : 1; - - switch(this.layout) { - case 'horizontal': - if(i >= this.colNum) i = 0; - i += 1; - if(isBreak) break; - cols[i-1].push(c); - colSize[i-1] += sz; - break; - case 'vertical': - if(isBreak){ - i += 1; - if(i >= this.colNum) - i = 0; - break; - } - cols[i].push(c); - colSize[i] += sz; - break; - case 'auto': - default: - if(isBreak) break; - cols[shortestCol()].push(c); - colSize[shortestCol()] += sz; - break; + #staging { + visibility: hidden; + height: 0; } - }); - - cols = cols.filter((c) => c.length > 0); - cols.forEach((c, i) => { - const div = document.createElement('div'); - div.classList.add('column'); - c.forEach((e) => div.appendChild(e)); - root.appendChild(div); - if(cols.length > 1 && typeof(this.maxWidth) === 'object') { - div.style.setProperty('max-width', this.maxWidth[i]); - } else { - div.style.setProperty('max-width', this.maxWidth+'px'); - } - }); - - this.cardSize = Math.max.apply(null, colSize); - } - - set hass(hass) { - this._hass = hass; - if(this._cards) - this._cards - .filter((c) => typeof(c) !== 'string') - .forEach((c) => c.hass = hass); - } - - getCardSize() { - return this.cardSize; - } - -} - -customElements.define('layout-card', LayoutCard); -}); -window.setTimeout(() => { - if(customElements.get('card-tools')) return; - customElements.define('layout-card', class extends HTMLElement{ - setConfig() { throw new Error("Can't find card-tools. See https://github.com/thomasloven/lovelace-card-tools");} - }); -}, 2000); + `}get _cardModder(){return{target:this}}})}]); \ No newline at end of file diff --git a/www/community/lovelace-layout-card/layout-card.js.gz b/www/community/lovelace-layout-card/layout-card.js.gz index 95f20739a3615c4b05335812697929a2577143dd..b528d4b2462e5b9287a39fae183ab6a0450e02b7 100644 GIT binary patch literal 3350 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 literal 1715 zcmV;k22A-MiwFpsb39!F|7>A-Z*_DnV_|Y+E^2cCtypbu+cpsXo?pRwKqW1<><%3; z*mVXh4bTrsS1cX&AqY}MqAa?yXqbwW3xv0E-MH(nw=}&$z*0* zm;c2Qk?l#|C@T2hI{XmU1bdzbCWVe_rk#}tJ0qbd$b81_Q zgScc56pV1$w(2Xrk&E0941L3jq}+s2lAPV)xXu~JS<=e)D#{_j8xox;YX5D`^285< z+0$DyGv!7@?MS#uNx_j*%*^!0a?OKI2qxuavoyXIh~a;ZRdt z6%}<=xV8B~tnx2v^9Pw}XR6Y}FBpPVQVvcj1% zIP=y74gq{h;#8Fd00S9gN!L=)FV~h2yIw`vP;9qBO+%hpO;O>wMDMll$309UzSyp5 znUYxHk;g@av-g5FX&M6{itA1<+ee#9aYa$ovW-LZtb;mjjKteOaZAW+%~JH4U5L>d zaG#vYNcfUHn33_Ae9EJkYMrbg?|;7_qSOhF)?&6>$vv~+Q3a`1h|_fCHc;A?jp?s` zui9D!dJ#CpIYVx$ULD9*`f^CKce<0pM@$=YauJCPs8d3rHg)Wd5pdE4o5i>qP1?HVvV3y`LXura}6?vWh8qbof!TuO0%gu4gvMOiv9pg*S4A zfi{`p>zy5Y5RQ*KKcvKPUGvPpx(>SXQ!+Ju;`*XP%CoW(6wa1%z;kDhPPZX61}(Kg z%f3NN+aNP)CcYlSh#-JUbnNriChEQ+%} z%;^9z{u{vRt15Ygd5X&hJs0cvgG4%cgE?*H;a}Zn1bNy{$mqZuCZOE9bl5SBBTk8j zLt+nTYftN?xLer?u#q?jr z+bTmpc+xDF^BEI&ZErhjRHJl$+Kk_Ps8@_^2tAaXxR4J2t>?TixmRG^Fy#`);lu+ik zPQ6j>RM)R_RW`L;-Y(HSyucaP?#vJ`n^T3GWhJ)0hm9lES-qiq>6XaU#+^{yjsJtN zV{d)m#ie(Q{o8iaxn(%=tuMRhu*sLM`9d*~6O^yVD|`AgQ&u-$wWFABW%kUaNv&hO zJnA6wl?{;=yNk7PXzA{3b>h@tel4&KQup5$}CKFlr{{ZgV J`yI^_00557R0;q9 diff --git a/www/community/lovelace-layout-card/webpack.config.js b/www/community/lovelace-layout-card/webpack.config.js new file mode 100644 index 0000000..e24daea --- /dev/null +++ b/www/community/lovelace-layout-card/webpack.config.js @@ -0,0 +1,10 @@ +const path = require('path'); + +module.exports = { + entry: './src/main.js', + mode: 'production', + output: { + filename: 'layout-card.js', + path: path.resolve(__dirname) + } +}; \ No newline at end of file diff --git a/www/community/lovelace-layout-card/webpack.config.js.gz b/www/community/lovelace-layout-card/webpack.config.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..d27989716ece02531d7bee8c36661fb3de1617c4 GIT binary patch literal 173 zcmV;e08;-SiwFqKob6o#|953#aA9L>E@N+QW@%?GYI6WJj?oIkAPhy{{ffNV!e-xF z_ANy;V-Bk>#<4Q`?@NWf1d?-ZxGty4P%J;-286E`2nT)cdR&y+vTqR$eo7{;qI?wq zCZ>G`ZKg_Y+H6SXS@lJT=qR9km5`ZYH$jpv>PxQB?zZl@RRF6` * { white-space: nowrap; @@ -36,10 +37,12 @@ class MultipleEntityRow extends Polymer.Element { } state-badge { flex: 0 0 40px; + cursor: pointer; } .entity { margin-right: 16px; text-align: center; + cursor: pointer; } .entity span { font-size: 10px; @@ -50,58 +53,93 @@ class MultipleEntityRow extends Polymer.Element { } .state { min-width: 45px; - text-align: end; } .toggle { margin-left: 8px; } - -
+ +
- [[entityName(_config)]] + [[entityName(main)]]
-