From f0bafd19c34c588fca5d45a0564ea60765a94e1a Mon Sep 17 00:00:00 2001 From: Cloos <36499953+Clooos@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:33:22 +0100 Subject: [PATCH] v1.5.2 --- src/bubble-card.js | 924 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 855 insertions(+), 69 deletions(-) diff --git a/src/bubble-card.js b/src/bubble-card.js index c011f07..0178182 100644 --- a/src/bubble-card.js +++ b/src/bubble-card.js @@ -1,64 +1,55 @@ -var version = 'v1.5.1'; +var version = 'v1.5.2'; let editor; let entityStates = {}; let lastCall = { entityId: null, stateChanged: null, timestamp: null }; -let resourceAdded = localStorage.getItem('resourceAdded') === 'true'; -let moduleAdded = localStorage.getItem('moduleAdded') === 'true'; -let browserCacheCleared = localStorage.getItem('browserCacheCleared') === 'true'; - -// Check if bubble-pop-up.js is installed as a module or as a resource -async function addResource(hass, url, moduleInstalled) { - let resources = await hass.callWS({ type: "lovelace/resources" }); - let resource = resources.find(r => r.url.includes("bubble-pop-up.js")); - - if (!moduleInstalled && !resource) { - await hass.callWS({ - type: "lovelace/resources/create", - url: url + '?v=' + version, - res_type: "module" - }); - - localStorage.setItem('resourceAdded', true); - localStorage.setItem('moduleAdded', false); - localStorage.setItem('browserCacheCleared', false); - moduleAdded = false; - browserCacheCleared = false; - - } else if (moduleInstalled && resource) { - await hass.callWS({ - type: "lovelace/resources/delete", - resource_id: resource.id - }); - localStorage.setItem('moduleAdded', true); - localStorage.setItem('resourceAdded', false); - localStorage.setItem('browserCacheCleared', false); - moduleAdded = true; - browserCacheCleared = false; - } else if (moduleInstalled && !resource) { - localStorage.setItem('moduleAdded', true); - } +class BubbleCard extends HTMLElement { + constructor() { + super(); + if (!window.eventAdded) { + // 'urlChanged' custom event + const pushState = history.pushState; + window.popUpInitialized = false; + + history.pushState = function () { + pushState.apply(history, arguments); + window.dispatchEvent(new Event('pushstate')); + }; - if (!browserCacheCleared) { - localStorage.setItem('browserCacheCleared', true); - location.reload(true); + const replaceState = history.replaceState; + history.replaceState = function () { + replaceState.apply(history, arguments); + window.dispatchEvent(new Event('replacestate')); + }; + + ['pushstate', 'replacestate', 'click', 'popstate', 'mousedown', 'touchstart'].forEach((eventType) => { + window.addEventListener(eventType, urlChanged); + }, { passive: true }); + + const event = new Event('urlChanged'); + + function urlChanged() { + const newUrl = window.location.href; + if (newUrl !== this.currentUrl) { + window.dispatchEvent(event); + this.currentUrl = newUrl; + } + } + + // Check url when pop-ups are initialized + const popUpInitialized = () => { + window.dispatchEvent(event); + window.addEventListener('popstate', urlChanged, { passive: true }); + }; + + window.addEventListener('popUpInitialized', popUpInitialized, { passive: true }); + + window.eventAdded = true; + } } -} - -class BubbleCard extends HTMLElement { + set hass(hass) { - // Initialize bubble-pop-up.js if it's not installed yet. - let moduleInstalled = this.config.module_installed; - - if ((!window.resourcesChecked && !resourceAdded && !moduleInstalled) || (!window.resourcesChecked && moduleInstalled && !moduleAdded)) { - console.log ("Resources checked"); - let url = "/hacsfiles/Bubble-Card/bubble-pop-up.js"; - addResource(hass, url, moduleInstalled); - resourceAdded = true; - window.resourcesChecked = true; - } - // Initialize the content if it's not there yet. if (!this.content) { this.attachShadow({ @@ -87,7 +78,7 @@ class BubbleCard extends HTMLElement { } let customStyles = !this.config.styles ? '' : this.config.styles; - let entityId = this.config.entity ? this.config.entity : ''; // && hass.states[this.config.entity] + let entityId = this.config.entity && hass.states[this.config.entity] ? this.config.entity : ''; let icon = !this.config.icon && this.config.entity ? hass.states[entityId].attributes.icon || hass.states[entityId].attributes.entity_picture || '' : this.config.icon || ''; let name = this.config.name ? this.config.name : this.config.entity ? hass.states[entityId].attributes.friendly_name : ''; let widthDesktop = this.config.width_desktop || '540px'; @@ -103,6 +94,10 @@ class BubbleCard extends HTMLElement { let marginCenter = this.config.margin ? (this.config.margin !== '0' ? this.config.margin : '0px') : '7px'; + let popUpHash = this.config.hash; + let popUpOpen; + let startTouchY; + let lastTouchY; // Check for edit mode this.editorElement ? editor = this.editorElement.classList.contains('edit-mode') : false; @@ -369,7 +364,6 @@ class BubbleCard extends HTMLElement { themeBgColor = themeBgColor || haStyle.getPropertyValue('--ha-card-background') || haStyle.getPropertyValue('--card-background-color'); let color = this.config.bg_color ? this.config.bg_color : themeBgColor; let bgOpacity = this.config.bg_opacity !== undefined ? this.config.bg_opacity : '88'; - let popUpOpen; function convertToRGBA(color, opacity) { let rgbaColor = ''; @@ -392,6 +386,552 @@ class BubbleCard extends HTMLElement { } switch (this.config.card_type) { + // Initialize pop-up card + case 'pop-up': + if (this.errorTriggered) { + return; + } + + if (!this.initStyleAdded && !this.host && !editor) { + // Hide vertical stack content before initialization + this.card.style.marginTop = '4000px'; + this.initStyleAdded = true; + } + + const createPopUp = () => { + if (!this.host || this.host !== this.getRootNode().host) { + this.host = this.getRootNode().host; + } else { + if (!this.popUp) { + this.verticalStack = this.getRootNode(); + this.popUp = this.verticalStack.querySelector('#root'); + + if (!window.popUpInitialized && this.popUp) { + const backOpen = this.config.back_open || false; + backOpen ? localStorage.setItem('backOpen', true) : localStorage.setItem('backOpen', false); + const backOpenState = localStorage.getItem('backOpen') === 'true'; + + if (backOpenState) { + window.backOpen = true; + const event = new Event('popUpInitialized'); + setTimeout(() => { + window.dispatchEvent(event); + }, 0); + } else { + window.backOpen = false; + popUpOpen = popUpHash + false; + history.replaceState(null, null, location.href.split('#')[0]); + } + window.popUpInitialized = true; + } + } + + const popUp = this.popUp; + const text = this.config.text || ''; + const stateEntityId = this.config.state; + formatedState = stateEntityId ? hass.formatEntityState(hass.states[stateEntityId]) + ' ' + text : text; + const marginTopMobile = this.config.margin_top_mobile + ? (this.config.margin_top_mobile !== '0' ? this.config.margin_top_mobile : '0px') + : '0px'; + const marginTopDesktop = this.config.margin_top_desktop + ? (this.config.margin_top_desktop !== '0' ? this.config.margin_top_desktop : '0px') + : '0px'; + const displayPowerButton = this.config.entity ? 'flex' : 'none'; + state = stateEntityId ? hass.states[stateEntityId].state : ''; + let closeTimeout; + let rgbaBgColor; + + if (!this.headerAdded) { + const headerContainer = document.createElement("div"); + headerContainer.setAttribute("id", "header-container"); + + const div = document.createElement("div"); + headerContainer.appendChild(div); + + const iconContainer = document.createElement("div"); + iconContainer.setAttribute("class", "header-icon"); + div.appendChild(iconContainer); + + createIcon(this, hass, entityId, icon, iconContainer); + addActions(this, iconContainer); + + const h2 = document.createElement("h2"); + h2.textContent = name; + div.appendChild(h2); + + const p = document.createElement("p"); + p.textContent = formatedState; + div.appendChild(p); + + const haIcon2 = document.createElement("ha-icon"); + haIcon2.setAttribute("class", "power-button"); + haIcon2.setAttribute("icon", "mdi:power"); + haIcon2.setAttribute("style", `display: ${displayPowerButton};`); + div.appendChild(haIcon2); + + const button = document.createElement("button"); + button.setAttribute("class", "close-pop-up"); + button.onclick = function() { history.replaceState(null, null, location.href.split('#')[0]); localStorage.setItem('isManuallyClosed_' + popUpHash, true); }; + headerContainer.appendChild(button); + + const haIcon3 = document.createElement("ha-icon"); + haIcon3.setAttribute("icon", "mdi:close"); + button.appendChild(haIcon3); + + this.content.appendChild(headerContainer); + this.header = div; + + this.headerAdded = true; + } else if (entityId) { + const iconContainer = this.content.querySelector("#header-container .header-icon"); + const h2 = this.content.querySelector("#header-container h2"); + const p = this.content.querySelector("#header-container p"); + const haIcon2 = this.content.querySelector("#header-container .power-button"); + + iconContainer.innerHTML = ''; // Clear the container + createIcon(this, hass, entityId, icon, iconContainer); + + h2.textContent = name; + p.textContent = formatedState; + haIcon2.setAttribute("style", `display: ${displayPowerButton};`); + } + + if (!this.eventAdded && !editor) { + window['checkHashRef_' + popUpHash] = checkHash; + window.addEventListener('urlChanged', window['checkHashRef_' + popUpHash], { passive: true }); + window.addEventListener('click', function(e) { + // Reset auto close + location.hash === popUpHash && resetAutoClose(); + + if (!window.justOpened) { + return; + } + + const target = e.composedPath(); + + if (target && + !target.some(el => el.nodeName === 'HA-MORE-INFO-DIALOG') && + !target.some(el => el.id === 'root' && !el.classList.contains('close-pop-up')) && + popUpOpen === popUpHash + true) { + popUpOpen = popUpHash + false; + history.replaceState(null, null, location.href.split('#')[0]); + localStorage.setItem('isManuallyClosed_' + popUpHash, true) + } + }, { passive: true }); + + this.eventAdded = true; + } + + function urlChangedHandler() { + window['checkHashRef_' + popUpHash]; + } + + function powerButtonClickHandler() { + toggleEntity(entityId); + } + + function windowClickHandler(e) { + // Reset auto close + if (window.hash === popUpHash) { + resetAutoClose(); + } + + if (!window.justOpened) { + return; + } + + const target = e.composedPath(); + + if (target && + !target.some(el => el.nodeName === 'HA-MORE-INFO-DIALOG') && + !target.some(el => el.id === 'root' && !el.classList.contains('close-pop-up')) && + popUpOpen === popUpHash + true) { + popUpOpen = popUpHash + false; + history.replaceState(null, null, location.href.split('#')[0]); + localStorage.setItem('isManuallyClosed_' + popUpHash, true) + } + } + + function windowKeydownHandler(e) { + if (e.key === 'Escape') { + popUpOpen = popUpHash + false; + history.replaceState(null, null, location.href.split('#')[0]); + localStorage.setItem('isManuallyClosed_' + popUpHash, true) + } + } + + function popUpTouchstartHandler(event) { + // Reset auto close + if (window.hash === popUpHash) { + resetAutoClose(); + } + + // Record the Y position of the finger at the start of the touch + startTouchY = event.touches[0].clientY; + lastTouchY = startTouchY; + } + + function popUpTouchmoveHandler(event) { + // Calculate the distance the finger has traveled + let touchMoveDistance = event.touches[0].clientY - startTouchY; + + // If the distance is positive (i.e., the finger is moving downward) and exceeds a certain threshold, close the pop-up + if (touchMoveDistance > 300 && event.touches[0].clientY > lastTouchY) { + popUpOpen = popUpHash + false; + history.replaceState(null, null, location.href.split('#')[0]); + popUpOpen = popUpHash + false; + localStorage.setItem('isManuallyClosed_' + popUpHash, true) + } + + // Update the Y position of the last touch + lastTouchY = event.touches[0].clientY; + } + + if (entityId) { + const rgbColor = hass.states[entityId].attributes.rgb_color; + this.rgbColor = rgbColor + ? (!isColorCloseToWhite(rgbColor) ? `rgb(${rgbColor})` : 'rgb(255,220,200)') + : (stateOn + ? (entityId.startsWith("light.") ? 'rgba(255,220,200, 0.5)' : 'var(--accent-color)') + : 'rgba(255, 255, 255, 1'); + this.rgbColorOpacity = rgbColor + ? (!isColorCloseToWhite(rgbColor) ? `rgba(${rgbColor}, 0.5)` : 'rgba(255,220,200, 0.5)') + : (entityId && stateOn + ? (entityId.startsWith("light.") ? 'rgba(255,220,200, 0.5)' : 'var(--accent-color)') + : 'var(--background-color,var(--secondary-background-color))'); + rgbaBgColor = convertToRGBA(color, 0); + this.iconFilter = rgbColor ? + (!isColorCloseToWhite(rgbColor) ? 'brightness(1.1)' : 'none') : + 'none'; + } + + function checkHash() { + if (!editor) { + window.hash = location.hash.split('?')[0]; + + // Open on hash change + if (window.hash === popUpHash) { + openPopUp(); + // Close on back button from browser + } else if (popUp.classList.contains('open-pop-up')) { + closePopUp(); + } + } + }; + + const content = this.content; + + function openPopUp() { + popUp.classList.remove('close-pop-up'); + popUp.classList.add('open-pop-up'); + content.querySelector('.power-button').addEventListener('click', powerButtonClickHandler, { passive: true }); + window.addEventListener('keydown', windowKeydownHandler, { passive: true }); + popUp.addEventListener('touchstart', popUpTouchstartHandler, { passive: true }); + popUp.addEventListener('touchmove', popUpTouchmoveHandler, { passive: true }); + popUpOpen = popUpHash + true; + setTimeout(() => { window.justOpened = true; }, 10); + resetAutoClose(); + } + + function closePopUp() { + popUp.classList.remove('open-pop-up'); + popUp.classList.add('close-pop-up'); + content.querySelector('.power-button').removeEventListener('click', powerButtonClickHandler); + window.removeEventListener('keydown', windowKeydownHandler); + popUp.removeEventListener('touchstart', popUpTouchstartHandler); + popUp.removeEventListener('touchmove', popUpTouchmoveHandler); + popUpOpen = popUpHash + false; + window.justOpened = false; + clearTimeout(closeTimeout); + } + + function resetAutoClose() { + // Clear any existing timeout + clearTimeout(closeTimeout); + // Start autoclose if enabled + if(autoClose > 0) { + closeTimeout = setTimeout(autoClosePopUp, autoClose); + } + } + + function autoClosePopUp(){ + history.replaceState(null, null, location.href.split('#')[0]); + } + + const popUpStyles = ` + ha-card { + margin-top: 0 !important; + background: none !important; + border: none !important; + } + .card-content { + width: 100% !important; + padding: 0 !important; + } + #root { + transition: all 1s !important; + position: fixed !important; + margin: 0 -${marginCenter}; /* 7px */ + width: 100%; + background-color: ${rgbaColor}; + box-shadow: 0px 0px 50px rgba(0,0,0,${shadowOpacity / 100}); + backdrop-filter: blur(${bgBlur}px); + -webkit-backdrop-filter: blur(${bgBlur}px); + border-radius: 42px; + box-sizing: border-box; + top: calc(120% + ${marginTopMobile} + var(--header-height)); + grid-gap: 12px !important; + gap: 12px !important; + grid-auto-rows: min-content; + padding: 18px 18px 220px 18px !important; + height: 100% !important; + -ms-overflow-style: none; /* for Internet Explorer, Edge */ + scrollbar-width: none; /* for Firefox */ + overflow-y: auto; + overflow-x: hidden; + z-index: 1 !important; /* Higher value hide the more-info panel */ + /* For older Safari but not working with Firefox */ + /* display: grid !important; */ + } + #root > bubble-card:first-child::after { + content: ''; + display: block; + position: sticky; + top: 0; + left: -50px; + margin: -70px 0 -36px -36px; + overflow: visible; + width: 200%; + height: 100px; + background: linear-gradient(0deg, ${rgbaBgColor} 0%, ${rgbaColor} 80%); + z-index: 0; + } + #root::-webkit-scrollbar { + display: none; /* for Chrome, Safari, and Opera */ + } + #root > bubble-card:first-child { + position: sticky; + top: 0; + z-index: 1; + background: none !important; + overflow: visible; + } + #root.open-pop-up { + /*will-change: transform;*/ + transform: translateY(-120%); + transition: transform .4s !important; + } + #root.open-pop-up > * { + /* Block child items to overflow and if they do clip them */ + /*max-width: calc(100vw - 38px);*/ + max-width: 100% !important; + overflow-x: clip; + } + #root.close-pop-up { + transform: translateY(-20%); + transition: transform .4s !important; + box-shadow: none; + } + @media only screen and (min-width: 768px) { + #root { + top: calc(120% + ${marginTopDesktop} + var(--header-height)); + width: calc(${widthDesktop}${widthDesktopDivided[2] === '%' && !isSidebarHidden ? ' - var(--mdc-drawer-width)' : ''}) !important; + left: calc(50% - ${widthDesktopDivided[1] / 2}${widthDesktopDivided[2]}); + margin: 0 !important; + } + } + @media only screen and (min-width: 870px) { + #root { + left: calc(50% - ${widthDesktopDivided[1] / 2}${widthDesktopDivided[2]} + ${isSidebarHidden ? '0px' : `var(--mdc-drawer-width) ${widthDesktopDivided[2] === '%' ? '' : '/ 2'}`}); + } + } + #root.editor { + position: inherit !important; + width: 100% !important; + padding: 18px !important; + } + `; + + const headerStyles = ` + ha-card { + margin-top: 0 !important; + } + #header-container { + display: inline-flex; + ${!icon && !name && !entityId && !state && !text ? 'flex-direction: row-reverse;' : ''} + width: 100%; + margin: 0; + padding: 0; + } + #header-container > div { + display: ${!icon && !name && !entityId && !state && !text ? 'none' : 'inline-flex'}; + align-items: center; + position: relative; + padding: 6px; + z-index: 1; + flex-grow: 1; + background-color: ${entityId ? this.rgbColorOpacity : 'var(--background-color,var(--secondary-background-color))'}; + transition: background 1s; + border-radius: 25px; + margin-right: 14px; + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); + } + .header-icon { + display: inline-flex; + width: 38px; + height: 38px; + background-color: var(--card-background-color,var(--ha-card-background)); + border-radius: 100%; + margin: 0 10px 0 0; + cursor: ${!this.config.entity && !this.config.double_tap_action && !this.config.tap_action && !this.config.hold_action ? 'default' : 'pointer'}; + flex-wrap: wrap; + align-content: center; + justify-content: center; + overflow: hidden; + } + .header-icon > ha-icon { + color: ${stateOn ? (this.rgbColor ? this.rgbColor : 'var(--accent-color)') : 'inherit'}; + opacity: ${stateOn ? '1' : '0.6'}; + filter: ${this.iconFilter}; + } + .header-icon::after { + content: ''; + position: absolute; + width: 38px; + height: 38px; + display: block; + opacity: 0.2; + transition: background-color 1s; + border-radius: 50%; + background-color: ${stateOn ? (this.rgbColor ? this.rgbColor : 'var(--accent-color)') : 'var(--card-background-color,var(--ha-card-background))'}; + } + .entity-picture { + height: calc(100% + 16px); + width: calc(100% + 16px); + } + #header-container h2 { + display: inline-flex; + margin: 0 18px 0 0; + /*line-height: 0px;*/ + z-index: 1; + font-size: 20px; + } + #header-container p { + display: inline-flex; + line-height: 0px; + font-size: 16px; + } + .power-button { + cursor: pointer; + flex-grow: inherit; + width: 24px; + height: 24px; + border-radius: 12px; + margin: 0 10px; + background: none !important; + justify-content: flex-end; + background-color: var(--background-color,var(--secondary-background-color)); + } + .close-pop-up { + height: 50px; + width: 50px; + border: none; + border-radius: 50%; + z-index: 1; + background: var(--background-color,var(--secondary-background-color)); + color: var(--primary-text-color); + flex-shrink: 0; + cursor: pointer; + } + `; + + addStyles(this, popUpStyles, customStyles, state, entityId, '', '', popUp); + addStyles(this, headerStyles, customStyles, state, entityId, stateChanged(entityId)); + + if (editor) { + popUp.classList.add('editor'); + popUp.classList.remove('open-pop-up'); + popUp.classList.remove('close-pop-up'); + } else { + popUp.classList.remove('editor'); + } + } + } + + const triggerEntity = this.config.trigger_entity ? this.config.trigger_entity : ''; + const triggerState = this.config.trigger_state ? this.config.trigger_state : ''; + const triggerClose = this.config.trigger_close ? this.config.trigger_close : false; + const stateEntity = this.config.state; + + if (this.popUp !== this.getRootNode().querySelector('#root')) { + let initPopUp = setInterval(() => { + createPopUp(); + if (this.popUp) { + clearInterval(initPopUp); + } + }, 20); + + setTimeout(() => { + if (!this.popUp) { + this.errorTriggered = true; + clearInterval(initPopUp); + throw new Error("Pop-up card must be placed inside a vertical_stack! If it's already the case, please ignore this error 🍻"); + } + }, 4000); + } else if (!editor && this.wasEditing) { + createPopUp(); + this.wasEditing = false; + } else if (((popUpHash === window.hash && (stateChanged(entityId) || stateChanged(stateEntity)))) || editor) { + createPopUp(); + if (editor) { + this.wasEditing = true; + } + } + + if (this.popUp && stateChanged(triggerEntity) && hass.states[triggerEntity]) { + if (localStorage.getItem('previousTriggerState_' + popUpHash) === null) { + localStorage.setItem('previousTriggerState_' + popUpHash, ''); + } + if (localStorage.getItem('isManuallyClosed_' + popUpHash) === null) { + localStorage.setItem('isManuallyClosed_' + popUpHash, 'false'); + } + if (localStorage.getItem('isTriggered_' + popUpHash) === null) { + localStorage.setItem('isTriggered_' + popUpHash, 'false'); + } + + let previousTriggerState = localStorage.getItem('previousTriggerState_' + popUpHash); + let isManuallyClosed = localStorage.getItem('isManuallyClosed_' + popUpHash) === 'true'; + let isTriggered = localStorage.getItem('isTriggered_' + popUpHash) === 'true'; + + if (hass.states[triggerEntity].state === triggerState && previousTriggerState === null && !isTriggered) { + navigate('', popUpHash); + isTriggered = true; + localStorage.setItem('isTriggered_' + popUpHash, isTriggered); + } + + if (hass.states[triggerEntity].state !== previousTriggerState) { + isManuallyClosed = false; + localStorage.setItem('previousTriggerState_' + popUpHash, hass.states[triggerEntity].state); + localStorage.setItem('isManuallyClosed_' + popUpHash, isManuallyClosed); + } + + if (hass.states[triggerEntity].state === triggerState && !isManuallyClosed) { + navigate('', popUpHash); + isTriggered = true; + localStorage.setItem('isTriggered_' + popUpHash, isTriggered); + } else if (hass.states[triggerEntity].state !== triggerState && triggerClose && this.popUp.classList.contains('open-pop-up') && isTriggered && !isManuallyClosed) { + history.replaceState(null, null, location.href.split('#')[0]); + popUpOpen = popUpHash + false; + isTriggered = false; + isManuallyClosed = true; + localStorage.setItem('isManuallyClosed_' + popUpHash, isManuallyClosed); + localStorage.setItem('isTriggered_' + popUpHash, isTriggered); + } + } + + break; + // Initialize horizontal buttons stack case 'horizontal-buttons-stack' : const createButton = (button, link, icon) => { @@ -1296,7 +1836,9 @@ class BubbleCard extends HTMLElement { setConfig(config) { if (config.card_type === 'pop-up') { - throw new Error('Breaking change: Since v1.5.0 you need to replace "custom:bubble-card" with "custom:bubble-pop-up" in all your pop-ups.'); + if (!config.hash) { + throw new Error("You need to define an hash. Please note that this card must be placed inside a vertical_stack to work as a pop-up."); + } } else if (config.card_type === 'horizontal-buttons-stack') { var definedLinks = {}; @@ -1321,6 +1863,13 @@ class BubbleCard extends HTMLElement { throw new Error("You need to define an entity"); } } + + // if (this.hass && config.entity) { + // if (!this.hass.states[config.entity]) { + // throw new Error("You need to define a valid entity"); + // } + // } + this.config = config; } @@ -1400,9 +1949,57 @@ class BubbleCardEditor extends LitElement { return this._config.state || ''; } + get _text() { + return this._config.text || ''; + } + + get _hash() { + return this._config.hash || '#pop-up-name'; + } + + get _trigger_entity() { + return this._config.trigger_entity || ''; + } + + get _trigger_state() { + return this._config.trigger_state || ''; + } + + get _trigger_close() { + return this._config.trigger_close || false; + } + + get _margin() { + return this._config.margin || '7px'; + } + + get _margin_top_mobile() { + return this._config.margin_top_mobile || '0px'; + } + + get _margin_top_desktop() { + return this._config.margin_top_desktop || '0px'; + } + get _width_desktop() { return this._config.width_desktop || '540px'; } + + get _bg_color() { + return this._config.bg_color || window.color; + } + + get _bg_opacity() { + return this._config.bg_opacity !== undefined ? this._config.bg_opacity : '88'; + } + + get _bg_blur() { + return this._config.bg_blur !== undefined ? this._config.bg_blur : '14'; + } + + get _shadow_opacity() { + return this._config.shadow_opacity !== undefined ? this._config.shadow_opacity : '0'; + } get _is_sidebar_hidden() { return this._config.is_sidebar_hidden || false; @@ -1411,6 +2008,14 @@ class BubbleCardEditor extends LitElement { get _rise_animation() { return this._config.rise_animation !== undefined ? this._config.rise_animation : true; } + + get _auto_close() { + return this._config.auto_close || ''; + } + + get _back_open() { + return this._config.back_open || false; + } get _icon_open() { return this._config.icon_open || ''; @@ -1420,14 +2025,6 @@ class BubbleCardEditor extends LitElement { return this._config.icon_close || ''; } - get _icon_down() { - return this._config.icon_down || ''; - } - - get _icon_up() { - return this._config.icon_up || ''; - } - get _open_service() { return this._config.open_service || 'cover.open_cover'; } @@ -1527,11 +2124,183 @@ class BubbleCardEditor extends LitElement { return html`
${this.makeDropdown("Card type", "card_type", cardTypeList)} -

Pop-up

- This card allows you to convert any vertical stack into a pop-up. Each pop-up can be opened by targeting its link (e.g. '#pop-up-name'), with navigation_path or with the horizontal buttons stack that is included. - Please note that the location of pop-ups has changed. They are now available as "Bubble Pop-Up" in the card selector on your dashboard. +

Pop-up + + Regular mode + +

+ This card allows you to convert any vertical stack into a pop-up. Each pop-up can be opened by targeting its link (e.g. '#pop-up-name'), with navigation_path or with the horizontal buttons stack that is included.
It must be placed within a vertical-stack card at the top most position to function properly. The pop-up will be hidden by default until you open it.

How to get the optimized mode?
+ + + ${this.makeDropdown("Optional - Icon", "icon")} + ${this.makeDropdown("Optional - Entity to toggle (e.g. room light group)", "entity", allEntitiesList)} + ${this.makeDropdown("Optional - Entity state to display (e.g. room temperature)", "state", allEntitiesList)} + + +

Pop-up trigger

+ This allows you to open this pop-up based on the state of any entity, for example you can open a "Security" pop-up with a camera when a person is in front of your house. You can also create a toggle helper (input_boolean) and trigger its opening/closing in an automation. + ${this.makeDropdown("Optional - Entity to open the pop-up based on its state", "trigger_entity", allEntitiesList)} + + + +
+ +
+
+

Styling options

+ + + + + + +
+ +
+
+ +
+ + +
+
+ + +
+
+ + +
+ You can't set a value to 0 with the sliders for now, just change it to 0 in the text field if you need to. +

Advanced settings

+ + +
+ +
+
+ Back button/event support : This allow you to navigate through your pop-ups history when you press the back button of your browser. This setting can be applied only once, you don't need to change it in all pop-ups. If it's not working just turn it on for each pop-ups. ${this.makeVersion()} -
+ `; } else if (this._config.card_type === 'button') { return html` @@ -1826,7 +2595,24 @@ class BubbleCardEditor extends LitElement { makeVersion() { return html` -

Bubble Card ${version}

+

+ Bubble Card + + ${version} + +

`; }