From 08472ab6734588f871e0fecf2c4fed8954999a2f Mon Sep 17 00:00:00 2001 From: Cloos <36499953+Clooos@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:18:57 +0100 Subject: [PATCH] v1.7.0 --- dist/bubble-card.js | 150 ++++++----- dist/bubble-pop-up-fix.js | 1 + src/bubble-card.ts | 36 ++- src/bubble-pop-up-fix.ts | 23 ++ src/cards/button.ts | 12 - src/cards/cover.ts | 27 +- src/cards/horizontal-buttons-stack.ts | 138 +++++----- src/cards/pop-up.ts | 359 ++++++++++++++++---------- src/cards/separator.ts | 48 +--- src/editor/bubble-card-editor.ts | 72 ++++-- src/tools/init.ts | 23 +- src/tools/style.ts | 16 +- src/tools/tap-actions.ts | 2 - src/tools/url-listener.ts | 20 +- src/var/cards.ts | 18 +- src/var/version.ts | 2 +- webpack.config.js | 17 +- 17 files changed, 543 insertions(+), 421 deletions(-) create mode 100644 dist/bubble-pop-up-fix.js create mode 100644 src/bubble-pop-up-fix.ts diff --git a/dist/bubble-card.js b/dist/bubble-card.js index 34ef680..c7ac27c 100644 --- a/dist/bubble-card.js +++ b/dist/bubble-card.js @@ -1,18 +1,9 @@ -(()=>{"use strict";var __webpack_modules__={946:(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{G:()=>getIconStyles,IU:()=>createIcon,L2:()=>addStyles,_k:()=>convertToRGBA,mk:()=>getIconColor,wW:()=>isColorCloseToWhite});let timeouts={},idCounter=0;const addStyles=function(hass,context,styles,customStyles,state,entityId,stateChanged,path="",element=context.content){const id=idCounter++,key=id+styles,executeStyles=()=>{const customStylesEval=customStyles?eval("`"+customStyles+"`"):"";let styleAddedKey=styles+"Added";if(!context[styleAddedKey]||context.previousStyle!==customStylesEval||stateChanged||context.previousConfig!==context.config){if(!context[styleAddedKey]){if(context.styleElement=element.querySelector("style"),!context.styleElement){context.styleElement=document.createElement("style");const e=path?element.querySelector(path):element;e?.appendChild(context.styleElement)}context[styleAddedKey]=!0}const e=document.createElement("style");e.innerHTML=customStylesEval+styles,context.styleElement.parentNode.insertBefore(e,context.styleElement.nextSibling),context.styleElement.parentNode.removeChild(context.styleElement),context.styleElement=e,context.previousStyle=customStylesEval,context.previousConfig=context.config}};timeouts[key]?clearTimeout(timeouts[key]):executeStyles(),timeouts[key]=setTimeout(executeStyles,500)};function createIcon(e,t,n,o,i){let a=e._hass,r=!(!t||!a.states[t].attributes)&&a.states[t].attributes;e.imageUrl=!!r.entity_picture&&r.entity_picture,updateIcon(e,a,t,n,o),i||setInterval((()=>{a=e._hass,t&&a.states[t]&&(e.currentEntityPicture=a.states[t].attributes.entity_picture,e.currentEntityPicture!==e.previousEntityPicture&&(e.imageUrl=e.currentEntityPicture,updateIcon(e,a,t,n,o),e.previousEntityPicture=e.currentEntityPicture))}),1e3)}function updateIcon(e,t,n,o,i){for(;i.firstChild;)i.removeChild(i.firstChild);let a=e.config.icon&&e.config.icon.includes("/")?e.config.icon:e.imageUrl?e.imageUrl:"";if(a&&(r=t.states[n].state,n.startsWith("media_player.")&&!["off","unknown","idle",void 0].includes(r)||!n.startsWith("media_player."))){const e=document.createElement("div");e.setAttribute("class","entity-picture"),e.setAttribute("alt","Icon"),i&&(i.appendChild(e),i.style.background="center / cover no-repeat url("+a+"), var(--card-background-color,var(--ha-card-background))")}else{const e=document.createElement("ha-icon");e.setAttribute("icon",o),e.setAttribute("class","icon"),i&&i.appendChild(e)}var r}function isColorCloseToWhite(e){let t=[220,220,190];for(let n=0;n<3;n++)if(e[n]{for(var n in t)__webpack_require__.o(t,n)&&!__webpack_require__.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var __webpack_exports__={};(()=>{let e="v1.6.4";const t=new Event("urlChanged");function n(){const e=function(){try{return document.querySelector("body > home-assistant").shadowRoot.querySelector("home-assistant-main").shadowRoot.querySelector("ha-drawer > partial-panel-resolver > ha-panel-lovelace").shadowRoot.querySelector("hui-root").shadowRoot.querySelector("div")}catch(e){return}}();if(e)return e.classList.contains("edit-mode")}var o=__webpack_require__(946);const i=(e,t,n,o)=>{o=o||{},n=null==n?{}:n;const i=new Event(t,{bubbles:void 0===o.bubbles||o.bubbles,cancelable:Boolean(o.cancelable),composed:void 0===o.composed||o.composed});return i.detail=n,e.dispatchEvent(i),i},a=e=>{i(window,"haptic",e)},r=(e,t,n=!1)=>{n?history.replaceState(null,"",t):history.pushState(null,"",t),i(window,"location-changed",{replace:n})};function s(e,t){e.callService("homeassistant","toggle",{entity_id:t})}const l=300;let c,d,p,h,u,g,m=null,b=0,f=0;class _{constructor(e,t,n){this.element=e,this.config=t,this.sendActionEvent=n,this.holdDuration=300,this.holdTimeout=null,this.tapTimeout=null,this.lastTap=0,this.holdTriggered=!1}handleStart(){this.holdTriggered=!1,this.holdTimeout=setTimeout((()=>{this.sendActionEvent(this.element,this.config,"hold"),this.holdTriggered=!0}),this.holdDuration)}handleEnd(e){clearTimeout(this.holdTimeout);let t=(new Date).getTime(),n=t-this.lastTap;n0?(clearTimeout(this.tapTimeout),this.sendActionEvent(this.element,this.config,"double_tap")):this.holdTriggered||(this.tapTimeout=setTimeout((()=>{this.sendActionEvent(this.element,this.config,"tap")}),this.holdDuration)),this.lastTap=t,this.holdTriggered=!1}handleCancel(){clearTimeout(this.holdTimeout),this.holdTriggered=!1}}function v(e,t,n){const o=Date.now();if("tap"===n&&o-fi.handleEnd(e)),{passive:!0}),e.addEventListener("touchcancel",i.handleCancel.bind(i),{passive:!0})):(e.addEventListener("mousedown",i.handleStart.bind(i),{passive:!0}),e.addEventListener("mouseup",(e=>i.handleEnd(e)),{passive:!0}),e.addEventListener("mouseout",i.handleCancel.bind(i),{passive:!0}))}function w(e,t,n,i){let a=t.styles?t.styles:"",r=t.entity&&n.states[t.entity]?t.entity:"",s=!t.icon&&r?n.states[r].attributes.icon||n.states[r].attributes.entity_picture||"":t.icon||"",l=t.name?t.name:r?n.states[r].attributes.friendly_name:"",u=t.width_desktop||"540px",g=u?u.match(/(\d+)(\D+)/):"",m=t.is_sidebar_hidden||!1,b=r?n.states[r].state:"";!function(e,t,n){e.hasState=t.states[n],e.hasState&&(e.newState=[e.hasState.state,e.hasState.attributes.rgb_color],e.oldState&&e.newState[0]===e.oldState[0]&&e.newState[1]===e.oldState[1]?e.stateChanged=!1:(e.oldState=e.newState,e.stateChanged=!0),e.stateChanged)}(e,n,r);let f=e.stateChanged,_=["on","open","cleaning","true","home","playing"].includes(b)||0!==Number(b)&&!isNaN(Number(b)),v=void 0===t.rise_animation||t.rise_animation,y=t.margin?"0"!==t.margin?t.margin:"0px":"7px",w=void 0!==t.bg_opacity?t.bg_opacity:"88",x=void 0!==t.shadow_opacity?t.shadow_opacity:"0",k=void 0!==t.bg_blur?t.bg_blur:"10",{iconColorOpacity:C,iconColor:$,iconFilter:S}=(0,o.mk)(n,r,_,o.wW),E=(0,o.G)(r,_,$,S),O=getComputedStyle(document.body),A=O.getPropertyValue("--ha-card-background")||O.getPropertyValue("--card-background-color"),L=t.bg_color?t.bg_color:A;if(L&&(!e.color||L!==e.color)){const t=1.02;d=(0,o._k)(L,w/100,t),document.body.style.setProperty("--bubble-pop-up-background",d),e.color=L,window.color=L}return{customStyles:a,entityId:r,icon:s,name:l,widthDesktop:u,widthDesktopDivided:g,isSidebarHidden:m,state:b,stateChanged:f,stateOn:_,formatedState:h,riseAnimation:v,marginCenter:y,popUpOpen:c,rgbaColor:d,rgbColor:p,bgOpacity:w,shadowOpacity:x,bgBlur:k,iconColorOpacity:C,iconColor:$,iconFilter:S,iconStyles:E,haStyle:O,themeBgColor:A,color:L}}function x(e){const t=e._hass;let{customStyles:s,entityId:l,icon:c,name:d,widthDesktop:p,widthDesktopDivided:h,isSidebarHidden:u,state:m,stateChanged:b,stateOn:f,riseAnimation:_,marginCenter:v,popUpOpen:y,rgbaColor:x,rgbColor:k,bgOpacity:C,shadowOpacity:$,bgBlur:S,iconColorOpacity:E,iconColor:O,iconFilter:A,iconStyles:L,haStyle:I,themeBgColor:T,color:D}=w(e,e.config,t);setInterval((()=>{g=n(),g&&!e.editorModeAdded?(e.buttonsContainer.classList.add("editor"),e.card.classList.add("editor"),e.editorModeAdded=!0):!g&&e.editorModeAdded&&(e.buttonsContainer.classList.remove("editor"),e.card.classList.remove("editor"),e.editorModeAdded=!1)}),100);if(!e.buttonsAdded){const t=document.createElement("div");t.classList.add("horizontal-buttons-stack-container"),e.content.appendChild(t),e.buttonsContainer=t}const V=(e,n,i)=>{if(t.states[n].attributes.rgb_color){const i=t.states[n].attributes.rgb_color,a=(0,o.wW)(i)?"rgba(255,220,200, 0.5)":`rgba(${i}, 0.5)`;e.style.backgroundColor=a,e.style.border="1px solid rgba(0,0,0,0)"}else t.states[n].attributes.rgb_color||"on"!=t.states[n].state?(e.style.backgroundColor="rgba(0,0,0,0)",e.style.border="1px solid var(--primary-text-color)"):(e.style.backgroundColor="rgba(255,255,255,0.5)",e.style.border="1px solid rgba(0,0,0,0)")};let M=[],z=1;for(;e.config[z+"_link"];){const t=z+"_",n=e.config[t+"name"]||"",o=e.config[t+"pir_sensor"];c=e.config[t+"icon"]||"";const i=e.config[t+"link"],a=e.config[t+"entity"];M.push({button:n,pirSensor:o,icon:c,link:i,lightEntity:a}),z++}if(e.config.auto_order&&M.sort(((e,n)=>e.pirSensor&&n.pirSensor?"on"===t.states[e.pirSensor].state&&"on"===t.states[n.pirSensor].state?t.states[e.pirSensor].last_updated{const o=((t,n,o)=>{const s=document.createElement("button");return s.setAttribute("class",`button ${n.substring(1)}`),s.innerHTML=`\n ${""!==o?``:""}\n ${""!==t?`

${t}

`:""}\n `,s.hasListener||(s.addEventListener("click",(e=>{y=location.hash+!0,y!==n+!0?(r(0,n),y=n+!0):(history.replaceState(null,null,location.href.split("#")[0]),i(window,"location-changed",!0),y=n+!1),a("light")}),{passive:!0}),window.addEventListener("urlChanged",(()=>{e.config.highlight_current_view&&(location.pathname===n||location.hash===n?s.classList.add("highlight"):s.classList.remove("highlight"))}),{passive:!0}),s.hasListener=!0),s})(n.button,n.link,n.icon);t[n.link]=o,e.buttonsContainer.appendChild(o)})),e.buttonsAdded=!0,e.buttons=t}let B=0;!function(e){if(e.buttonsUpdated&&!g)return;let t=[];for(let n of M)e.buttons[n.link]&&(t.push(localStorage.getItem(`buttonWidth-${n.link}`)),t.push(localStorage.getItem(`buttonContent-${n.link}`)));Promise.all(t).then((t=>{let n=0;for(let o of M){let i=e.buttons[o.link];if(i){let a=t[n],r=t[n+1];n+=2,a&&"0"!==a&&r===i.innerHTML&&!g||(a=i.offsetWidth,localStorage.setItem(`buttonWidth-${o.link}`,a),localStorage.setItem(`buttonContent-${o.link}`,i.innerHTML),e.previousConfig=e.config),i.style.transform=`translateX(${B}px)`,B+=parseInt(a)+12}o.lightEntity&&V(i,o.lightEntity,o.link)}e.buttonsAdded=!0}))}(e);const U=`\n ha-card {\n border-radius: 0;\n }\n .horizontal-buttons-stack {\n width: 100%;\n margin-top: 0 !important;\n background: none !important;\n position: fixed;\n height: 51px;\n bottom: 16px;\n left: ${v};\n z-index: 1 !important; /* Higher value hide the more-info panel */\n }\n @keyframes from-bottom {\n 0% {transform: translateY(200px);}\n 20% {transform: translateY(200px);}\n 46% {transform: translateY(-8px);}\n 56% {transform: translateY(1px);}\n 62% {transform: translateY(-2px);}\n 70% {transform: translateY(0);}\n 100% {transform: translateY(0);}\n }\n .horizontal-buttons-stack-container {\n width: max-content;\n position: relative;\n height: 51px;\n }\n .button {\n display: inline-flex;\n position: absolute;\n box-sizing: border-box !important;\n border: 1px solid var(--primary-text-color);\n align-items: center;\n height: 50px;\n line-height: 16px;\n white-space: nowrap;\n width: auto;\n border-radius: 25px;\n z-index: 1;\n padding: 0 16px;\n background: none;\n transition: background-color 1s, border 1s, transform 1s;\n color: var(--primary-text-color);\n }\n .highlight {\n animation: pulse 1.4s infinite alternate;\n }\n @keyframes pulse {\n 0% {\n filter: brightness(0.7);\n }\n 100% {\n filter: brightness(1.3);\n }\n }\n .icon {\n height: 24px;\n }\n .card-content {\n width: calc(100% + 18px);\n box-sizing: border-box !important;\n margin: 0 -36px !important;\n padding: 0 36px !important;\n overflow: scroll !important;\n -ms-overflow-style: none;\n scrollbar-width: none;\n -webkit-mask-image: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, 1) calc(0% + 28px), rgba(0, 0, 0, 1) calc(100% - 28px), transparent 100%);\n /* mask-image: linear-gradient(90deg, transparent 2%, rgba(0, 0, 0, 1) 6%, rgba(0, 0, 0, 1) 96%, transparent 100%); */\n /* -webkit-mask-image: linear-gradient(90deg, transparent 2%, rgba(0, 0, 0, 1) 6%, rgba(0, 0, 0, 1) 96%, transparent 100%); */\n }\n .horizontal-buttons-stack::before {\n content: '';\n position: absolute;\n top: -32px;\n left: -100%;\n display: block;\n background: linear-gradient(0deg, var(--background-color, var(--primary-background-color)) 50%, rgba(79, 69, 87, 0));\n width: 200%;\n height: 100px;\n }\n .card-content::-webkit-scrollbar {\n display: none;\n }\n @media only screen and (min-width: 600px) {\n .card-content {\n position: fixed;\n width: ${p} !important;\n left: calc(50% - ${h[1]/2}${h[2]});\n margin-left: -13px !important;\n padding: 0 26px !important;\n }\n }\n @media only screen and (min-width: 870px) {\n .card-content {\n position: fixed;\n width: calc(${p}${"%"!==h[2]||u?"":" - var(--mdc-drawer-width)"}) !important;\n left: calc(50% - ${h[1]/2}${h[2]} + ${!0===u?"0px":"var(--mdc-drawer-width) "+("%"===h[2]?"":"/ 2")});\n margin-left: -13px !important;\n padding: 0 26px !important;\n }\n }\n .horizontal-buttons-stack.editor {\n position: relative;\n bottom: 0;\n left: 0;\n overflow: hidden;\n }\n .horizontal-buttons-stack.editor::before {\n top: -32px;\n left: -100%;\n background: none;\n width: 100%;\n height: 0;\n }\n .horizontal-buttons-stack-container.editor > .button {\n transition: background-color 0s, border 0s, transform 0s;\n }\n .horizontal-buttons-stack-container.editor {\n margin-left: 1px;\n }\n .horizontal-buttons-stack.editor > .card-content {\n position: relative;\n width: calc(100% + 26px) !important;\n left: -26px;\n margin: 0 !important;\n padding: 0;\n }\n `;!window.hasAnimated&&_&&(e.content.style.animation="from-bottom 1.3s forwards",window.hasAnimated=!0,setTimeout((()=>{e.content.style.animation="none"}),1500)),(0,o.L2)(t,e,U,s)}const k=Object.getPrototypeOf(customElements.get("ha-panel-lovelace")),C=k.prototype.html,$=k.prototype.css;let S;!function(){if(!window.eventAdded){function e(){let e=0;const n=setInterval((()=>{e<10?(window.dispatchEvent(t),e++):clearInterval(n)}),100)}window.eventAdded=!0,window.popUpInitialized=!1,["location-changed","connection-status","popstate"].forEach((t=>{window.addEventListener(t,e)}),{passive:!0});const n=()=>{window.dispatchEvent(t)};window.addEventListener("popUpInitialized",n,{passive:!0})}}();class E extends HTMLElement{set hass(e){var t;switch(this._hass=e,S=n(),this.editor=S,async function(e){if(!window.resourcesChecked){window.resourcesChecked=!0;try{let t=(await e.callWS({type:"lovelace/resources"})).find((e=>e.url.includes("bubble-pop-up.js")));async function n(e){let t=await e.callWS({type:"lovelace/resources"}),n=t.findIndex((e=>e.url.includes("bubble-card.js"))),o=null;if(-1!==n&&0!==n){o=t.splice(n,1)[0];for(let n of t)await e.callWS({type:"lovelace/resources/delete",resource_id:n.id});o&&-1===(await e.callWS({type:"lovelace/resources"})).findIndex((e=>e.url.includes("bubble-card.js")))&&await e.callWS({type:"lovelace/resources/create",res_type:o.type,url:o.url});for(let n of t)await e.callWS({type:"lovelace/resources/create",res_type:n.type,url:n.url})}}t&&await e.callWS({type:"lovelace/resources/delete",resource_id:t.id}),n(e)}catch(o){throw o}}}(e),(t=this).content||(t.attachShadow({mode:"open"}),t.shadowRoot.innerHTML='\n \n
\n
\n
\n ',t.card=t.shadowRoot.querySelector("ha-card"),t.content=t.shadowRoot.querySelector("div")),this.config.card_type){case"pop-up":!function(e){const t=e._hass,n=e.editor,a=e.config;if(!t)return;let l,c,d,p,{customStyles:h,entityId:g,icon:m,name:b,widthDesktop:f,widthDesktopDivided:_,isSidebarHidden:v,state:x,stateChanged:k,stateOn:C,formatedState:$,riseAnimation:S,marginCenter:E,popUpOpen:O,rgbaColor:A,rgbColor:L,bgOpacity:I,shadowOpacity:T,bgBlur:D,iconColorOpacity:V,iconColor:M,iconFilter:z,iconStyles:B,haStyle:U,themeBgColor:q,color:H}=w(e,a,t),W=a.auto_close||!1,F=a.hash,Y=a.trigger_entity?a.trigger_entity:"",R=a.trigger_state?a.trigger_state:"",P=!!a.trigger_close&&a.trigger_close,N=a.entity?"flex":"none",j=a.text||"",X=a.state,G=a.close_on_click||!1,K=a.margin_top_mobile&&"0"!==a.margin_top_mobile?a.margin_top_mobile:"0px",Z=a.margin_top_desktop&&"0"!==a.margin_top_desktop?a.margin_top_desktop:"0px";if(x=X&&t.states[X]?t.states[X].state:"",e.errorTriggered)return;function J(){history.replaceState(null,null,location.href.split("#")[0]),i(window,"location-changed",!0)}function Q(e){window.hash===F&&ee();const t=e.composedPath();!t||t.some((e=>"HA-MORE-INFO-DIALOG"===e.nodeName))||t.some((e=>"root"===e.id&&!e.classList.contains("close-pop-up")))||setTimeout((function(){window.hash===F&&(J(),localStorage.setItem("isManuallyClosed_"+F,!0))}),100)}function ee(){clearTimeout(d),W>0&&(d=setTimeout(J,W))}function te(){s(t,g)}function ne(e){"Escape"===e.key&&(J(),localStorage.setItem("isManuallyClosed_"+F,!0))}function oe(e){window.hash===F&&ee(),l=e.touches[0].clientY,c=l}function ie(e){e.touches[0].clientY-l>300&&e.touches[0].clientY>c&&(J(),localStorage.setItem("isManuallyClosed_"+F,!0)),c=e.touches[0].clientY}function ae(){n||(window.hash=location.hash.split("?")[0],window.hash===F&&O!==F+!0?(e.popUp.classList.remove("close-pop-up"),e.popUp.classList.add("open-pop-up"),e.content.querySelector(".power-button").addEventListener("click",te,{passive:!0}),window.addEventListener("keydown",ne,{passive:!0}),e.popUp.addEventListener("touchstart",oe,{passive:!0}),e.popUp.addEventListener("touchmove",ie,{passive:!0}),document.body.style.overflow="hidden",re(e.popUp,!1),ee(),G&&(e.popUp.addEventListener("mouseup",J,{passive:!0}),e.popUp.addEventListener("touchend",J,{passive:!0})),O=F+!0,setTimeout((function(){window.addEventListener("click",Q,{passive:!0})}),10)):window.hash!==F&&O!==F+!1&&(e.popUp.classList.remove("open-pop-up"),e.popUp.classList.add("close-pop-up"),e.content.querySelector(".power-button").removeEventListener("click",te),window.removeEventListener("keydown",ne),window.removeEventListener("click",Q),e.popUp.removeEventListener("touchstart",oe),e.popUp.removeEventListener("touchmove",ie),document.body.style.overflow="",clearTimeout(d),G&&(e.popUp.removeEventListener("mouseup",J),e.popUp.removeEventListener("touchend",J)),O=F+!1,setTimeout((function(){re(e.popUp,!0)}),320)))}function re(e,t){for(var n=e.querySelectorAll("video"),o=0;o0&&!n[o].paused&&!n[o].ended&&n[o].readyState>n[o].HAVE_CURRENT_DATA;t&&i?n[o].pause():t||i||(n[o].play(),n[o].currentTime>0&&(n[o].currentTime=1e4))}var a=e.querySelectorAll("*");for(o=0;o :first-child::after {\n content: '';\n display: block;\n position: sticky;\n top: 0;\n left: -50px;\n margin: -70px 0 -36px -36px;\n overflow: visible;\n width: 200%;\n height: 100px;\n background: linear-gradient(0deg, ${p} 0%, ${A} 80%);\n z-index: 0;\n } \n #root::-webkit-scrollbar {\n display: none; /* for Chrome, Safari, and Opera */\n }\n #root > :first-child {\n position: sticky;\n top: 0;\n z-index: 1;\n background: none !important;\n overflow: visible;\n }\n #root.open-pop-up {\n transform: translateY(-120%);\n }\n #root.open-pop-up > * {\n /* Block child items to overflow and if they do clip them */\n /*max-width: calc(100vw - 38px);*/\n max-width: 100% !important;\n /*overflow-x: clip;*/\n }\n #root.close-pop-up { \n transform: translateY(-20%);\n box-shadow: none;\n }\n @media only screen and (min-width: 600px) {\n #root {\n top: calc(120% + ${Z} + var(--header-height));\n width: calc(${f}${"%"!==_[2]||v?"":" - var(--mdc-drawer-width)"}) !important;\n left: calc(50% - ${_[1]/2}${_[2]});\n margin: 0 !important;\n }\n } \n @media only screen and (min-width: 870px) {\n #root {\n left: calc(50% - ${_[1]/2}${_[2]} + ${v?"0px":"var(--mdc-drawer-width) "+("%"===_[2]?"":"/ 2")});\n }\n } \n #root.editor {\n position: inherit !important;\n width: 100% !important;\n padding: 18px !important;\n backdrop-filter: none !important;\n }\n `,s=`\n ${B}\n\n ha-card {\n margin-top: 0 !important;\n }\n #header-container {\n display: inline-flex;\n ${m||b||g||x||j?"":"flex-direction: row-reverse;"}\n height: 50px;\n width: 100%;\n margin: 0;\n padding: 0;\n }\n #header-container > div {\n display: ${m||b||g||x||j?"inline-flex":"none"};\n align-items: center;\n position: relative;\n padding-right: 6px;\n z-index: 1;\n flex-grow: 1;\n background-color: ${g?e.rgbColorOpacity:"var(--background-color,var(--secondary-background-color))"};\n transition: background 1s;\n border-radius: 25px;\n margin-right: 14px;\n backdrop-filter: blur(14px);\n -webkit-backdrop-filter: blur(14px);\n }\n #header-container h2 {\n display: inline-flex;\n margin: 0 18px 0 0;\n padding: 4px;\n z-index: 1;\n font-size: 18px;\n }\n #header-container p {\n display: inline-flex;\n font-size: 16px;\n min-width: fit-content ;\n }\n .power-button {\n cursor: pointer; \n flex-grow: inherit; \n width: 24px;\n height: 24px;\n border-radius: 12px;\n margin: 0 10px;\n background: none !important;\n justify-content: flex-end;\n background-color: var(--background-color,var(--secondary-background-color));\n }\n .close-pop-up {\n height: 50px;\n width: 50px;\n border: none;\n border-radius: 50%;\n z-index: 1;\n background: var(--background-color,var(--secondary-background-color));\n color: var(--primary-text-color);\n flex-shrink: 0;\n cursor: pointer;\n }\n `;(0,o.L2)(t,e,r,h,x,g,"","",i),(0,o.L2)(t,e,s,h,x,g,k)}e.initStyleAdded||e.popUp||n||(e.card.style.marginTop="4000px",e.initStyleAdded=!0);const le=setTimeout((()=>{const t=new Event("popUpInitialized");e.element||(e.element=e.getRootNode().querySelector("#root")),e.element&&(!e.popUp||k||n&&!e.editorModeAdded)?(e.popUp=e.element,n&&e.popUp&&!e.editorModeAdded?(e.popUp.classList.add("editor"),e.popUp.classList.remove("close-pop-up","open-pop-up"),se(),e.editorModeAdded=!0):se(),clearTimeout(le),window.dispatchEvent(t)):!n&&e.popUp&&e.editorModeAdded&&(e.popUp.classList.remove("editor"),se(),e.editorModeAdded=!1)}),0);e.popUp&&Y&&function(t){if(!t||t===u)return;u=t,null===localStorage.getItem("previousTriggerState_"+F)&&localStorage.setItem("previousTriggerState_"+F,""),null===localStorage.getItem("isManuallyClosed_"+F)&&localStorage.setItem("isManuallyClosed_"+F,"false"),null===localStorage.getItem("isTriggered_"+F)&&localStorage.setItem("isTriggered_"+F,"false");let n=localStorage.getItem("previousTriggerState_"+F),o="true"===localStorage.getItem("isManuallyClosed_"+F),i="true"===localStorage.getItem("isTriggered_"+F);t!==R||null!==n||i||(r(0,F),i=!0,localStorage.setItem("isTriggered_"+F,i)),t!==n&&(o=!1,localStorage.setItem("previousTriggerState_"+F,t),localStorage.setItem("isManuallyClosed_"+F,o)),t!==R||o?t!==R&&P&&e.popUp.classList.contains("open-pop-up")&&i&&!o&&(J(),i=!1,o=!0,localStorage.setItem("isManuallyClosed_"+F,o),localStorage.setItem("isTriggered_"+F,i)):(r(0,F),i=!0,localStorage.setItem("isTriggered_"+F,i))}(t.states[Y].state)}(this);break;case"horizontal-buttons-stack":x(this);break;case"button":!function(e){const t=e._hass,n=e.editor;let{customStyles:i,entityId:r,icon:l,name:c,widthDesktop:d,widthDesktopDivided:p,isSidebarHidden:h,state:u,stateChanged:g,stateOn:m,formatedState:b,riseAnimation:f,marginCenter:_,popUpOpen:v,rgbaColor:x,rgbColor:k,bgOpacity:C,shadowOpacity:$,bgBlur:S,iconColorOpacity:E,iconColor:O,iconFilter:A,iconStyles:L,haStyle:I,themeBgColor:T,color:D}=w(e,e.config,t);b=r&&(g||n)?t.formatEntityState(t.states[r]):"";let V=e.config.button_type||"switch",M=!!e.config.show_state&&e.config.show_state,z=r?t.states[r].attributes.brightness||0:"",B=r?t.states[r].attributes.volume_level||0:"",U=!1,q=z,H=B,W=0,F=0,Y=0,R=!1,P=null;if(e.config.service_on,e.config.service_off,!e.buttonAdded){const t=document.createElement("div");t.setAttribute("class","button-container"),e.content.appendChild(t)}const N=document.createElement("div");N.setAttribute("class","icon-container"),e.iconContainer=N;const j=document.createElement("div");j.setAttribute("class","name-container");const X=document.createElement("div");X.setAttribute("class","switch-button");const G=document.createElement("div");G.setAttribute("class","range-slider");const K=document.createElement("div");if(K.setAttribute("class","range-fill"),!e.buttonContainer||n){if(n&&e.buttonContainer){for(;e.buttonContainer.firstChild;)e.buttonContainer.removeChild(e.buttonContainer.firstChild);e.eventAdded=!1,e.wasEditing=!0}e.buttonContainer=e.content.querySelector(".button-container"),"slider"!==V||e.buttonAdded&&!n?("switch"===V||"custom"===V||n)&&(e.buttonContainer.appendChild(X),X.appendChild(N),X.appendChild(j),e.switchButton=e.content.querySelector(".switch-button")):(e.buttonContainer.appendChild(G),G.appendChild(N),G.appendChild(j),G.appendChild(K),e.rangeFill=e.content.querySelector(".range-fill")),(0,o.IU)(e,r,l,N,n),j.innerHTML=`\n

${c}

\n ${M?`

${b}

`:""}\n `,e.buttonAdded=!0}function Z(e){a("success");let t=e.querySelector(".feedback-element");t||(t=document.createElement("div"),t.setAttribute("class","feedback-element"),e.appendChild(t)),t.style.animation="tap-feedback .5s",setTimeout((()=>{t.style.animation="none",e.removeChild(t)}),500)}function J(e){W=e.pageX||(e.touches?e.touches[0].pageX:0),F=e.pageY||(e.touches?e.touches[0].pageY:0),Y=G.value,e.target!==N&&e.target!==N.querySelector("ha-icon")&&(U=!0,document.addEventListener("mouseup",ee,{passive:!0}),document.addEventListener("touchend",ee,{passive:!0}),document.addEventListener("mousemove",Q,{passive:!0}),document.addEventListener("touchmove",Q,{passive:!0}),P=setTimeout((()=>{oe(e.pageX||e.touches[0].pageX),te(),P=null}),200))}function Q(e){const t=e.pageX||(e.touches?e.touches[0].pageX:0),n=e.pageY||(e.touches?e.touches[0].pageY:0);Math.abs(n-F)>Math.abs(t-W)?(clearTimeout(P),ee()):(document.removeEventListener("mousemove",Q),document.removeEventListener("touchmove",Q),document.addEventListener("mousemove",ne,{passive:!0}),document.addEventListener("touchmove",ne,{passive:!0}))}function ee(){U=!1,R=!1,te(),document.removeEventListener("mouseup",ee),document.removeEventListener("touchend",ee),document.removeEventListener("mousemove",ne),document.removeEventListener("touchmove",ne)}function te(){r.startsWith("light.")?(z=q,t.callService("light","turn_on",{entity_id:r,brightness:z})):r.startsWith("media_player.")&&(B=H,t.callService("media_player","volume_set",{entity_id:r,volume_level:B}))}function ne(e){const t=e.pageX||(e.touches?e.touches[0].pageX:0),n=e.pageY||(e.touches?e.touches[0].pageY:0);U&&Math.abs(t-W)>10?(a("light"),oe(t)):U&&Math.abs(n-F)>10&&(U=!1,G.value=Y)}function oe(e){const t=G.getBoundingClientRect(),n=Math.min(Math.max(e-t.left,0),t.width)/t.width;r.startsWith("light.")?q=Math.round(255*n):r.startsWith("media_player.")&&(H=n),K.style.transition="none",K.style.transform=`translateX(${100*n}%)`}M&&b&&(e.content.querySelector(".state").textContent=b),e.eventAdded||"switch"!==V?e.eventAdded||"slider"!==V?e.eventAdded||"custom"!==V||(X.addEventListener("click",(()=>Z(e.switchButton)),{passive:!0}),y(X,e.config),e.eventAdded=!0):(G.addEventListener("mousedown",J,{passive:!0}),G.addEventListener("touchstart",J,{passive:!0}),y(N,e.config),e.eventAdded=!0):(X.addEventListener("click",(()=>Z(e.switchButton)),{passive:!0}),X.addEventListener("click",(function(e){e.target!==N&&e.target!==N.querySelector("ha-icon")&&s(t,r)}),{passive:!0}),y(N,e.config),e.eventAdded=!0),e.isDragging||"slider"!==V||(e.rangeFill.style.transition="all .3s",r.startsWith("light.")?e.rangeFill.style.transform=`translateX(${z/255*100}%)`:r.startsWith("media_player.")&&(e.rangeFill.style.transform=`translateX(${100*B}%)`));const ie=`\n ha-card {\n margin-top: 0 !important;\n background: none !important;\n opacity: ${"unavailable"!==u?"1":"0.5"};\n }\n \n .button-container {\n position: relative;\n width: 100%;\n height: 50px;\n z-index: 0;\n background-color: var(--background-color-2,var(--secondary-background-color));\n border-radius: 25px;\n mask-image: radial-gradient(white, black);\n -webkit-mask-image: radial-gradient(white, black);\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n -webkit-transform: translateZ(0);\n overflow: hidden;\n }\n \n .switch-button,\n .range-slider {\n display: inline-flex;\n position: absolute;\n height: 100%;\n width: 100%;\n transition: background-color 1.5s;\n background-color: ${m&&["switch","custom"].includes(V)?"var(--accent-color)":"rgba(0,0,0,0)"};\n }\n\n .range-fill {\n z-index: -1;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n background-color: ${E};\n width: 100%;\n left: -100%;\n }\n \n .switch-button {\n cursor: pointer !important;\n }\n \n .range-slider {\n cursor: ew-resize;\n }\n \n .name-container {\n position: relative;\n display: ${M?"block":"inline-flex"};\n margin-left: 4px;\n z-index: 1;\n font-weight: 600;\n align-items: center;\n line-height: ${M?"4px":"16px"};\n padding-right: 16px;\n }\n \n .state {\n font-size: 12px;\n opacity: 0.7;\n }\n \n .feedback-element {\n position: absolute;\n top: 0;\n left: 0;\n opacity: 0;\n width: 100%;\n height: 100%;\n background-color: rgb(0,0,0);\n }\n \n @keyframes tap-feedback {\n 0% {transform: translateX(-100%); opacity: 0;}\n 64% {transform: translateX(0); opacity: 0.1;}\n 100% {transform: translateX(100%); opacity: 0;}\n }\n\n ${L}\n `;(0,o.L2)(t,e,ie,i,u,r,g)}(this);break;case"separator":!function(e){const t=e._hass,n=e.editor,i=e.config;let{customStyles:a,entityId:r,icon:s,name:l,widthDesktop:c,widthDesktopDivided:d,isSidebarHidden:p,state:h,stateChanged:u,stateOn:g,formatedState:m,riseAnimation:b,marginCenter:f,popUpOpen:_,rgbaColor:v,rgbColor:y,bgOpacity:x,shadowOpacity:k,bgBlur:C,iconColorOpacity:$,iconColor:S,iconFilter:E,iconStyles:O,haStyle:A,themeBgColor:L,color:I}=w(e,i,t);if(!e.separatorAdded||n){if(n&&e.separatorContainer)for(;e.separatorContainer.firstChild;)e.separatorContainer.removeChild(e.separatorContainer.firstChild);e.separatorAdded||(e.separatorContainer=document.createElement("div"),e.separatorContainer.setAttribute("class","separator-container")),e.separatorContainer.innerHTML=`\n
\n \n

${l}

\n
\n
\n `,e.content.appendChild(e.separatorContainer),e.separatorAdded=!0}(0,o.L2)(t,e,"\n .separator-container {\n display: inline-flex;\n width: 100%;\n margin-top: 12px;\n }\n .separator-container div:first-child {\n display: inline-flex;\n max-width: calc(100% - 38px);\n }\n .separator-container div ha-icon {\n display: inline-flex;\n height: 24px;\n width: 24px;\n margin: 0 22px 0 8px;\n transform: translateY(-2px);\n }\n .separator-container div h4 {\n display: inline-flex;\n margin: 0 20px 0 0;\n font-size: 16px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .separator-container div:last-child {\n display: inline-flex; \n border-radius: 6px; \n opacity: 0.5; \n margin-left: 10px; \n flex-grow: 1; \n height: 6px; \n align-self: center; \n background-color: var(--background-color,var(--secondary-background-color));\n }\n ",a)}(this);break;case"cover":!function(e){const t=e._hass,n=e.editor,i=e.config;let{customStyles:a,entityId:r,icon:s,name:l,widthDesktop:c,widthDesktopDivided:d,isSidebarHidden:p,state:h,stateChanged:u,stateOn:g,formatedState:m,riseAnimation:b,marginCenter:f,popUpOpen:_,rgbaColor:v,rgbColor:x,bgOpacity:k,shadowOpacity:C,bgBlur:$,iconColorOpacity:S,iconColor:E,iconFilter:O,iconStyles:A,haStyle:L,themeBgColor:I,color:T}=w(e,i,t);const D=i.icon_open?i.icon_open:"mdi:window-shutter-open",V=i.icon_close?i.icon_close:"mdi:window-shutter",M=i.open_service?i.open_service:"cover.open_cover",z=i.close_service?i.close_service:"cover.close_cover",B=i.stop_service?i.stop_service:"cover.stop_cover",U=i.icon_up?i.icon_up:"mdi:arrow-up",q=i.icon_down?i.icon_down:"mdi:arrow-down",H=!!e.config.show_state&&e.config.show_state;if(s="open"===t.states[i.entity].state?D:V,m=u?t.formatEntityState(t.states[r]):m||"",!e.coverAdded||n){if(n&&e.coverContainer)for(;e.coverContainer.firstChild;)e.coverContainer.removeChild(e.coverContainer.firstChild);e.coverContainer=document.createElement("div"),e.coverContainer.setAttribute("class","cover-container"),e.coverContainer.innerHTML=`\n
\n
\n
\n
\n

${l}

\n

\n
\n
\n
\n \n \n \n
\n `,e.content.appendChild(e.coverContainer);const o=e.coverContainer.querySelector(".open"),a=e.coverContainer.querySelector(".stop"),s=e.coverContainer.querySelector(".close");o.addEventListener("click",(()=>{t.callService(M.split(".")[0],M.split(".")[1],{entity_id:r})}),{passive:!0}),a.addEventListener("click",(()=>{t.callService(B.split(".")[0],B.split(".")[1],{entity_id:r})}),{passive:!0}),s.addEventListener("click",(()=>{t.callService(z.split(".")[0],z.split(".")[1],{entity_id:r})}),{passive:!0}),e.iconContainer=e.content.querySelector(".icon-container"),y(e.iconContainer,i),e.coverAdded=!0}e.iconContainer&&(u||n)&&(e.iconContainer.innerHTML=``,e.content.querySelector(".state").textContent=H?m:""),(0,o.L2)(t,e,"\n ha-card {\n margin-top: 0 !important;\n background: none !important;\n }\n \n .header-container {\n display: flex;\n align-items: center;\n margin-bottom: 10px;\n }\n \n .cover-container {\n display: grid;\n }\n \n .icon-container {\n display: flex;\n margin: 0 !important;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n /*z-index: 1;*/\n width: 48px;\n height: 48px;\n margin: 6px;\n border-radius: 50%;\n background-color: var(--card-background-color,var(--ha-card-background));\n border: 6px solid var(--background-color-2,var(--secondary-background-color));\n box-sizing: border-box;\n }\n \n .name-container {\n font-weight: 600;\n margin-left: 10px;\n line-height: 4px;\n }\n \n .buttons-container {\n display: grid;\n align-self: center;\n grid-auto-flow: column;\n grid-gap: 18px; \n }\n \n .state {\n font-size: 12px;\n opacity: 0.7;\n }\n \n ha-icon {\n display: flex; \n height: 24px; \n width: 24px; \n color: var(--primary-text-color);\n }\n \n .button {\n display: flex;\n background: var(--background-color-2,var(--secondary-background-color));\n height: 42px;\n border-radius: 32px;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border: none;\n }\n ",a,h,r)}(this);break;case"empty-column":!function(e){if(!e.emptyCollumnAdded){const t=document.createElement("div");t.setAttribute("class","empty-column"),t.innerHTML='\n
\n ',e.content.appendChild(t),e.emptyColumnAdded=!0}}(this)}window.columnFix||(window.columnFix=this.config.column_fix)}setConfig(e){if("pop-up"===e.card_type){if(!e.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("horizontal-buttons-stack"===e.card_type){var t={};for(var n in e)if(n.match(/^\d+_icon$/)){var o=n.replace("_icon","_link");if(void 0===e[o])throw new Error("You need to define "+o);if(t[e[o]])throw new Error("You can't use "+e[o]+" twice");t[e[o]]=!0}}else if(("button"===e.card_type||"cover"===e.card_type)&&!e.entity)throw new Error("You need to define an entity");if(window.entityError)throw new Error("You need to define a valid entity");this.config=e}getCardSize(){return window.columnFix?0:-1}static getConfigElement(){return document.createElement("bubble-card-editor")}}customElements.define("bubble-card",E),customElements.define("bubble-card-editor",class extends k{setConfig(e){this._config={...e}}static get properties(){return{hass:{},_config:{}}}get _card_type(){return this._config.card_type||""}get _button_type(){return this._config.button_type||"switch"}get _entity(){return this._config.entity||""}get _name(){return this._config.name||""}get _icon(){return this._config.icon||""}get _state(){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||!1}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 void 0!==this._config.bg_opacity?this._config.bg_opacity:"88"}get _bg_blur(){return void 0!==this._config.bg_blur?this._config.bg_blur:"14"}get _shadow_opacity(){return void 0!==this._config.shadow_opacity?this._config.shadow_opacity:"0"}get _is_sidebar_hidden(){return this._config.is_sidebar_hidden||!1}get _rise_animation(){return void 0===this._config.rise_animation||this._config.rise_animation}get _auto_close(){return this._config.auto_close||""}get _close_on_click(){return this._config.close_on_click||!1}get _icon_open(){return this._config.icon_open||""}get _icon_close(){return this._config.icon_close||""}get _open_service(){return this._config.open_service||"cover.open_cover"}get _close_service(){return this._config.open_service||"cover.close_cover"}get _stop_service(){return this._config.open_service||"cover.stop_cover"}get _auto_order(){return this._config.auto_order||!1}get _highlightCurrentview(){return this._config.highlight_current_view||!1}get _show_state(){return this._config.show_state||!1}render(){if(!this.hass)return C``;if(!this.listsUpdated){const e=e=>({label:e,value:e});this.allEntitiesList=Object.keys(this.hass.states).map(e),this.lightList=Object.keys(this.hass.states).filter((e=>"light"===e.substr(0,e.indexOf(".")))).map(e),this.sensorList=Object.keys(this.hass.states).filter((e=>"sensor"===e.substr(0,e.indexOf(".")))).map(e),this.binarySensorList=Object.keys(this.hass.states).filter((e=>"binary_sensor"===e.substr(0,e.indexOf(".")))).map(e),this.coverList=Object.keys(this.hass.states).filter((e=>"cover"===e.substr(0,e.indexOf(".")))).map(e),this.cardTypeList=[{label:"Button",value:"button"},{label:"Cover",value:"cover"},{label:"Empty column",value:"empty-column"},{label:"Horizontal buttons stack",value:"horizontal-buttons-stack"},{label:"Pop-up",value:"pop-up"},{label:"Separator",value:"separator"}],this.buttonTypeList=[{label:"Switch",value:"switch"},{label:"Slider",value:"slider"}],this.listsUpdated=!0}const t=this.allEntitiesList,n=(this.lightList,this.sensorList,this.coverList),o=this.cardTypeList,i=this.buttonTypeList;if("pop-up"===this._config.card_type)return C` +(()=>{"use strict";let t="v1.7.0";const e=new Event("urlChanged");function n(){const t=function(){try{return document.querySelector("body > home-assistant").shadowRoot.querySelector("home-assistant-main").shadowRoot.querySelector("ha-drawer > partial-panel-resolver > ha-panel-lovelace").shadowRoot.querySelector("hui-root").shadowRoot.querySelector("div")}catch(t){return}}();if(t)return t.classList.contains("edit-mode")}let i={},o=0;const a=function(t,e,n,a,r,s,l,d="",c=e.content){const p=o+++n,h=()=>{const i=a?Function("hass","entityId","state","return `"+a+"`;")(t,s,r):"";let o=n+"Added";if(!e[o]||e.previousStyle!==i||l||e.previousConfig!==e.config){if(!e[o]){if(e.styleElement=c.querySelector("style"),!e.styleElement){e.styleElement=document.createElement("style");const t=d?c.querySelector(d):c;t?.appendChild(e.styleElement)}e[o]=!0}const t=document.createElement("style");t.innerHTML=i+n,e.styleElement.parentNode.insertBefore(t,e.styleElement.nextSibling),e.styleElement.parentNode.removeChild(e.styleElement),e.styleElement=t,e.previousStyle=i,e.previousConfig=e.config}};i[p]?clearTimeout(i[p]):h(),i[p]=setTimeout(h,500)};function r(t,e,n,i,o){let a=t._hass,r=!(!e||!a.states[e].attributes)&&a.states[e].attributes;t.imageUrl=!!r.entity_picture&&r.entity_picture,s(t,a,e,n,i),o||setInterval((()=>{a=t._hass,e.startsWith("media_player.")&&e&&a.states[e]&&(t.currentEntityPicture=a.states[e].attributes.entity_picture,t.currentEntityPicture!==t.previousEntityPicture&&(t.imageUrl=t.currentEntityPicture,s(t,a,e,n,i),t.previousEntityPicture=t.currentEntityPicture))}),1e3)}function s(t,e,n,i,o){for(;o.firstChild;)o.removeChild(o.firstChild);let a=t.config.icon&&t.config.icon.includes("/")?t.config.icon:t.imageUrl?t.imageUrl:"";if(a&&(r=e.states[n].state,n.startsWith("media_player.")&&!["off","unknown","idle",void 0].includes(r)||!n.startsWith("media_player."))){const t=document.createElement("div");t.setAttribute("class","entity-picture"),t.setAttribute("alt","Icon"),o&&(o.appendChild(t),o.style.background="center / cover no-repeat url("+a+"), var(--card-background-color,var(--ha-card-background))")}else{const t=document.createElement("ha-icon");t.setAttribute("icon",i),t.setAttribute("class","icon"),o&&o.appendChild(t)}var r}function l(t){let e=[220,220,190];for(let n=0;n<3;n++)if(t[n]{i=i||{},n=null==n?{}:n;const o=new Event(e,{bubbles:void 0===i.bubbles||i.bubbles,cancelable:Boolean(i.cancelable),composed:void 0===i.composed||i.composed});return o.detail=n,t.dispatchEvent(o),o},h=t=>{p(window,"haptic",t)},u=(t,e,n=!1)=>{n?history.replaceState(null,"",e):history.pushState(null,"",e),p(window,"location-changed",{replace:n})};function g(t,e){t.callService("homeassistant","toggle",{entity_id:e})}const m=300;let b,f,v,_,y,w,x=null,k=0,C=0;class ${constructor(t,e,n){this.element=t,this.config=e,this.sendActionEvent=n,this.holdDuration=300,this.holdTimeout=null,this.tapTimeout=null,this.lastTap=0,this.holdTriggered=!1}handleStart(){this.holdTriggered=!1,this.holdTimeout=setTimeout((()=>{this.sendActionEvent(this.element,this.config,"hold"),this.holdTriggered=!0}),this.holdDuration)}handleEnd(t){clearTimeout(this.holdTimeout);let e=(new Date).getTime(),n=e-this.lastTap;n0?(clearTimeout(this.tapTimeout),this.sendActionEvent(this.element,this.config,"double_tap")):this.holdTriggered||(this.tapTimeout=setTimeout((()=>{this.sendActionEvent(this.element,this.config,"tap")}),this.holdDuration)),this.lastTap=e,this.holdTriggered=!1}handleCancel(){clearTimeout(this.holdTimeout),this.holdTriggered=!1}}function S(t,e,n){const i=Date.now();if("tap"===n&&i-C{const e=new Event("hass-action",{bubbles:!0,composed:!0});e.detail={config:o,action:n},t.dispatchEvent(e)}),1),x=n,k=i,"double_tap"===n&&(C=i)}function E(t,e,n,i){let o=new $(t,e,S);"ontouchstart"in window?(t.addEventListener("touchstart",o.handleStart.bind(o),{passive:!0}),t.addEventListener("touchend",(t=>o.handleEnd(t)),{passive:!0}),t.addEventListener("touchcancel",o.handleCancel.bind(o),{passive:!0})):(t.addEventListener("mousedown",o.handleStart.bind(o),{passive:!0}),t.addEventListener("mouseup",(t=>o.handleEnd(t)),{passive:!0}),t.addEventListener("mouseout",o.handleCancel.bind(o),{passive:!0}))}function L(t,e,n,i){let o=e.styles?e.styles:"",a=e.entity&&n.states[e.entity]?e.entity:"",r=a?n.states[a].state:"";!function(t,e,n){t.hasState=e.states[n],t.hasState&&(t.newState=[t.hasState.state,t.hasState.attributes.rgb_color],t.oldState&&t.newState[0]===t.oldState[0]&&t.newState[1]===t.oldState[1]?t.stateChanged=!1:(t.oldState=t.newState,t.stateChanged=!0),t.stateChanged)}(t,n,a);let s=t.stateChanged,d=!e.icon&&a?n.states[a].attributes.icon||n.states[a].attributes.entity_picture||"":e.icon||"",p=e.name?e.name:a?n.states[a].attributes.friendly_name:"",h=e.width_desktop||"540px",u=h?h.match(/(\d+)(\D+)/):"",g=e.is_sidebar_hidden||!1,m=["on","open","cleaning","true","home","playing"].includes(r)||0!==Number(r)&&!isNaN(Number(r)),x=void 0===e.rise_animation||e.rise_animation,k=e.margin?"0"!==e.margin?e.margin:"0px":"7px",C=void 0!==e.bg_opacity?e.bg_opacity:"88",$=void 0!==e.shadow_opacity?e.shadow_opacity:"0",S=void 0!==e.bg_blur?e.bg_blur:"10",{iconColorOpacity:E,iconColor:L,iconFilter:O}=function(t,e,n,i,o){let a,r,s;return e&&e.startsWith("light.")?(a=(o=t.states[e].attributes.rgb_color)?i(o)?"rgba(255,220,200,0.5)":`rgba(${o}, 0.5)`:n?"rgba(255,220,200, 0.5)":"rgba(255, 255, 255, 0.5)",r=o?i(o)?"rgb(255,220,200)":`rgb(${o})`:n?"rgba(255,220,200, 1)":"rgba(255, 255, 255, 1)",s=o?i(o)?"none":"brightness(1.1)":"none"):(a="var(--accent-color)",s="brightness(1.1)"),{iconColorOpacity:a,iconColor:r,iconFilter:s}}(n,a,m,l),A=function(t,e,n,i){return`\n .icon-container {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-content: center;\n justify-content: center;\n z-index: 1;\n min-width: 38px;\n min-height: 38px;\n margin: 6px;\n border-radius: 50%;\n cursor: pointer !important;\n background-color: var(--card-background-color,var(--ha-card-background));\n }\n \n .icon-container::after {\n content: '';\n position: absolute;\n display: block;\n opacity: ${t.startsWith("light.")?"0.2":"0"};\n width: 100%;\n height: 100%;\n transition: all 1s;\n border-radius: 50%;\n background-color: ${e?n||"var(--accent-color)":"var(--card-background-color,var(--ha-card-background))"};\n }\n \n .icon {\n display: flex;\n width: 22px; \n color: ${e?n||"var(--accent-color)":"inherit"} !important;\n opacity: ${e?"1":t?"0.6":"1"};\n filter: ${e?n?i:"brightness(1.1)":"inherit"};\n }\n \n .entity-picture {\n display: flex;\n height: 38px;\n width: 38px;\n border-radius: 100%;\n }\n `}(a,m,L,O);y=y?"":getComputedStyle(document.body),w=w?"":y.getPropertyValue("--ha-card-background")||y.getPropertyValue("--card-background-color");let T=e.bg_color?e.bg_color:w;return!T||f&&f===t.color||(f=c(T,C/100,1.02),t.color=f,window.color=T),{customStyles:o,entityId:a,icon:d,name:p,widthDesktop:h,widthDesktopDivided:u,isSidebarHidden:g,state:r,stateChanged:s,stateOn:m,formatedState:_,riseAnimation:x,marginCenter:k,popUpOpen:b,rgbaColor:f,rgbColor:v,bgOpacity:C,shadowOpacity:$,bgBlur:S,iconColorOpacity:E,iconColor:L,iconFilter:O,iconStyles:A,haStyle:y,themeBgColor:w,color:T}}let O,A,T=[];function I(t){const e=t._hass;let{customStyles:i,icon:o,name:r,widthDesktop:s,widthDesktopDivided:d,isSidebarHidden:c,riseAnimation:g,marginCenter:m,popUpOpen:b}=L(t,t.config,e),f=(setInterval((()=>{A=n(),A&&!t.editorModeAdded?(t.buttonsContainer.classList.add("editor"),t.card.classList.add("editor"),t.editorModeAdded=!0):!A&&t.editorModeAdded&&(t.buttonsContainer.classList.remove("editor"),t.card.classList.remove("editor"),t.editorModeAdded=!1)}),100),!!t.config.hide_gradient);if(!t.buttonsAdded){const e=document.createElement("div");e.classList.add("horizontal-buttons-stack-container"),t.content.appendChild(e),t.buttonsContainer=e}const v=(t,n)=>{const i=t.background;if(e.states[n].attributes.rgb_color){const t=e.states[n].attributes.rgb_color,o=l(t)?"rgba(255,220,200, 0.5)":`rgba(${t}, 0.5)`;i.style.backgroundColor=o,i.style.border="1px solid rgba(0,0,0,0)"}else e.states[n].attributes.rgb_color||"on"!=e.states[n].state?(i.style.backgroundColor="rgba(0,0,0,0)",i.style.border="1px solid var(--primary-text-color)"):(i.style.backgroundColor="rgba(255,255,255,0.5)",i.style.border="1px solid rgba(0,0,0,0)")};let _=[],y=1;for(;t.config[y+"_link"];){const e=y+"_",n=t.config[e+"name"]||"",i=t.config[e+"pir_sensor"];o=t.config[e+"icon"]||"";const a=t.config[e+"link"],r=t.config[e+"entity"];_.push({button:n,pirSensor:i,icon:o,link:a,lightEntity:r}),y++}if(t.config.auto_order&&_.sort(((t,n)=>t.pirSensor&&n.pirSensor?"on"===e.states[t.pirSensor].state&&"on"===e.states[n.pirSensor].state?e.states[t.pirSensor].last_updated{const i=((e,n,i)=>{const o=document.createElement("div");o.setAttribute("class",`button ${n.substring(1)}`),o.innerHTML=`\n ${""!==i?``:""}\n ${""!==e?`

${e}

`:""}\n
\n `;const a=document.createElement("div");return a.setAttribute("class","color-background"),o.appendChild(a),o.background=a,o.hasListener||(o.addEventListener("click",(t=>{b=location.hash+!0,b!==n+!0?(u(0,n),b=n+!0):(history.replaceState(null,null,location.href.split("#")[0]),p(window,"location-changed",!0),b=n+!1),h("light")}),{passive:!0}),window.addEventListener("urlChanged",(()=>{t.config.highlight_current_view&&(location.pathname===n||location.hash===n?o.classList.add("highlight"):o.classList.remove("highlight"))}),{passive:!0}),o.hasListener=!0),o})(n.button,n.link,n.icon);e[n.link]=i,t.buttonsContainer.appendChild(i)})),t.buttonsAdded=!0,t.buttons=e}let w=0;t.buttonsUpdated&&!A||(function(t){let e=[];for(let n of _)t.buttons[n.link]&&(e.push(localStorage.getItem(`buttonWidth-${n.link}`)),e.push(localStorage.getItem(`buttonContent-${n.link}`)));t.previousConfig||(t.previousConfig=t.config),Promise.all(e).then((e=>{let n=0;for(let i of _){let o=t.buttons[i.link];if(o){let a=e[n];e[n+1],n+=2,a&&"0"!==a&&!A||(a=o.offsetWidth,localStorage.setItem(`buttonWidth-${i.link}`,a),localStorage.setItem(`buttonContent-${i.link}`,o.innerHTML),t.previousConfig=t.config),w!==o.previousPosition&&(o.style.transform=`translateX(${w}px)`,o.previousPosition=w),w+=parseInt(a)+12}i.lightEntity&&v(o,i.lightEntity,i.link)}}))}(t),t.buttonsAdded=!0);const x=`\n ha-card {\n border-radius: 0;\n }\n .horizontal-buttons-stack {\n width: 100%;\n margin-top: 0 !important;\n /*background: none !important;*/\n position: fixed;\n height: 51px;\n bottom: 16px;\n left: ${m};\n z-index: 1 !important; /* Higher value hide the more-info panel */\n }\n @keyframes from-bottom {\n 0% {transform: translateY(100px);}\n 26% {transform: translateY(-8px);}\n 46% {transform: translateY(1px);}\n 62% {transform: translateY(-2px);}\n 70% {transform: translateY(0);}\n 100% {transform: translateY(0);}\n }\n .horizontal-buttons-stack-container {\n width: max-content;\n position: relative;\n height: 51px;\n }\n .button {\n display: inline-flex;\n position: absolute;\n box-sizing: border-box !important;\n /*border: 1px solid var(--primary-text-color);*/\n align-items: center;\n height: 50px;\n line-height: 16px;\n white-space: nowrap;\n width: auto;\n border-radius: 25px;\n z-index: 1;\n padding: 0 16px;\n color: var(--primary-text-color);\n transition: background-color 1s, border 1s, transform 1s;\n }\n .color-background {\n border-radius: 24px;\n width: 100%;\n height: 100%;\n box-sizing: border-box !important;\n position: absolute;\n left: 0;\n top: 0;\n z-index: -1;\n border: 1px solid var(--primary-text-color);\n transition: background-color 1s, border 1s, transform 1s;\n }\n .background {\n opacity: 0.8;\n border-radius: 24px;\n width: 100%;\n height: 100%;\n box-sizing: border-box !important;\n position: absolute;\n left: 0;\n z-index: -2;\n background-color: var(--background-color,var(--primary-background-color));\n }\n .highlight {\n animation: pulse 1.4s infinite alternate;\n }\n @keyframes pulse {\n 0% {\n filter: brightness(0.7);\n }\n 100% {\n filter: brightness(1.3);\n }\n }\n .icon {\n height: 24px;\n }\n .card-content {\n width: calc(100% + 18px);\n box-sizing: border-box !important;\n margin: 0 -36px !important;\n padding: 0 36px !important;\n overflow: scroll !important;\n -ms-overflow-style: none;\n scrollbar-width: none;\n -webkit-mask-image: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, 1) calc(0% + 28px), rgba(0, 0, 0, 1) calc(100% - 28px), transparent 100%);\n }\n .horizontal-buttons-stack::before {\n content: '';\n position: absolute;\n top: -32px;\n left: -100%;\n display: ${f?"none":"block"};\n background: linear-gradient(0deg, var(--background-color, var(--primary-background-color)) 50%, rgba(79, 69, 87, 0));\n width: 200%;\n height: 100px;\n }\n .card-content::-webkit-scrollbar {\n display: none;\n }\n @media only screen and (min-width: 600px) {\n .card-content {\n position: fixed;\n width: ${s} !important;\n left: calc(50% - ${d[1]/2}${d[2]});\n margin-left: -13px !important;\n padding: 0 26px !important;\n }\n }\n @media only screen and (min-width: 870px) {\n .card-content {\n position: fixed;\n width: calc(${s}${"%"!==d[2]||c?"":" - var(--mdc-drawer-width)"}) !important;\n left: calc(50% - ${d[1]/2}${d[2]} + ${!0===c?"0px":"var(--mdc-drawer-width) "+("%"===d[2]?"":"/ 2")});\n margin-left: -13px !important;\n padding: 0 26px !important;\n }\n }\n .horizontal-buttons-stack.editor {\n position: relative;\n bottom: 0;\n left: 0;\n overflow: hidden;\n }\n .horizontal-buttons-stack.editor::before {\n background: none;\n }\n .horizontal-buttons-stack-container.editor > .button,\n .horizontal-buttons-stack-container.editor > .button > .color-background {\n transition: background-color 0s, border 0s, transform 0s !important;\n }\n .horizontal-buttons-stack-container.editor {\n margin-left: 1px;\n }\n .horizontal-buttons-stack.editor > .card-content {\n position: relative;\n width: calc(100% + 26px) !important;\n left: -26px;\n margin: 0 !important;\n padding: 0;\n }\n `;!window.hasAnimated&&g&&(t.content.style.animation="from-bottom .6s forwards",window.hasAnimated=!0,setTimeout((()=>{t.content.style.animation="none"}),1500)),a(e,t,x,i)}window.openPopups=0;const V=Object.getPrototypeOf(customElements.get("ha-panel-lovelace")),z=V.prototype.html,M=V.prototype.css;let D;!function(){if(!window.eventAdded){function t(){window.dispatchEvent(e)}console.log("Event added"),window.eventAdded=!0,window.popUpInitialized=!1,["location-changed","connection-status","popstate"].forEach((e=>{window.addEventListener(e,t)}),{passive:!0});const n=()=>{window.dispatchEvent(e)};window.addEventListener("popUpInitialized",n,{passive:!0})}}();class U extends HTMLElement{set hass(t){switch(function(t){if(!t.content){let e=t.shadowRoot||t.attachShadow({mode:"open"}),n=document.createDocumentFragment(),i=document.createElement("ha-card");i.style.cssText="background: none; border: none; box-shadow: none; border-radius: 16px;";let o=document.createElement("div");o.className="card-content",o.style.padding="0",i.appendChild(o),n.appendChild(i),e.appendChild(n),t.card=i,t.content=o}}(this),this._hass=t,D=n(),this.editor=D,this.config.card_type){case"pop-up":!function(t){const e=t.editor;if(t.initStyleAdded||t.popUp||e||(t.card.style.marginTop="4000px",t.initStyleAdded=!0),t.errorTriggered)return;const n=t._hass,i=t.config,o=setTimeout((()=>{const n=new Event("popUpInitialized");t.verticalStack?clearTimeout(o):t.verticalStack=t.getRootNode(),t.verticalStack&&(!t.popUp||_||t.stateEntityChanged||e&&!t.editorModeAdded)?(t.popUp||(t.popUp=t.verticalStack.querySelector("#root"),t.popUp.setAttribute("class","pop-up")),e&&!t.editorModeAdded&&(t.popUp.classList.add("editor"),t.popUp.classList.remove("close-pop-up","open-pop-up"),t.editorModeAdded=!0),pt(),window.dispatchEvent(n)):!e&&t.popUp&&t.editorModeAdded&&(t.popUp.classList.remove("editor"),pt(),t.editorModeAdded=!1)}),0);let{customStyles:s,entityId:d,icon:h,name:m,widthDesktop:b,widthDesktopDivided:f,isSidebarHidden:v,stateChanged:_,stateOn:y,formatedState:w,marginCenter:x,popUpOpen:k,rgbaColor:C,rgbColor:$,bgOpacity:S,shadowOpacity:A,bgBlur:I,iconColorOpacity:V,iconColor:z,iconFilter:M,iconStyles:D,themeBgColor:U,color:B}=L(t,i,n),H=i.auto_close||!1,P=i.hash,F=i.entity?"flex":"none",Y=i.text||"",W=i.state,q=i.close_on_click||!1,N=i.hide_backdrop||!1,R=i.hide_card||"";!window.hideBackdrop&&N&&(window.hideBackdrop=!0);let j,X,G,Z=t.config.background_camera||!1,J=i.margin_top_mobile&&"0"!==i.margin_top_mobile?i.margin_top_mobile:"0px",K=i.margin_top_desktop&&"0"!==i.margin_top_desktop?i.margin_top_desktop:"0px",Q=W&&n.states[W]?n.states[W].state:"";function tt(){history.replaceState(null,null,location.href.split("#")[0]),p(window,"location-changed",!0)}function et(t){window.hash===P&&nt();const e=t.composedPath();!e||e.some((t=>"HA-MORE-INFO-DIALOG"===t.nodeName))||e.some((t=>"HA-DIALOG-DATE-PICKER"===t.nodeName))||e.some((t=>"root"===t.id&&!t.classList.contains("close-pop-up")))||setTimeout((function(){window.hash===P&&(tt(),localStorage.setItem("isManuallyClosed_"+P,!0))}),100)}function nt(){clearTimeout(G),H>0&&(G=setTimeout(tt,H))}function it(){g(n,d)}function ot(t){"Escape"===t.key&&(tt(),localStorage.setItem("isManuallyClosed_"+P,!0))}function at(t){window.hash===P&&nt(),j=t.touches[0].clientY,X=j}function rt(t){t.touches[0].clientY-j>300&&t.touches[0].clientY>X&&(tt(),localStorage.setItem("isManuallyClosed_"+P,!0)),X=t.touches[0].clientY}function st(){!e&&t.popUp&&(window.hash=location.hash.split("?")[0],window.hash!==P||k===P+!0||t.popUp.classList.contains("open-pop-up")?window.hash!==P&&k!==P+!1&&t.popUp.classList.contains("open-pop-up")&&(k=P+!1,window.openPopups--,t.popUp.classList.remove("open-pop-up"),t.popUp.classList.add("close-pop-up"),ct(),t.content.querySelector(".power-button").removeEventListener("click",it),window.removeEventListener("keydown",ot),window.removeEventListener("click",et),t.popUp.removeEventListener("touchstart",at),t.popUp.removeEventListener("touchmove",rt),document.body.style.overflow="",clearTimeout(G),q&&(t.popUp.removeEventListener("mouseup",tt),t.popUp.removeEventListener("touchend",tt)),setTimeout((function(){lt(t,t.popUp,!0)}),320)):(k=P+!0,window.openPopups++,t.popUp.classList.remove("close-pop-up"),t.popUp.classList.add("open-pop-up"),ct(),t.content.querySelector(".power-button").addEventListener("click",it,{passive:!0}),window.addEventListener("keydown",ot,{passive:!0}),t.popUp.addEventListener("touchstart",at,{passive:!0}),t.popUp.addEventListener("touchmove",rt,{passive:!0}),document.body.style.overflow="hidden",lt(t,t.popUp,!1),nt(),setTimeout((function(){q&&(t.popUp.addEventListener("mouseup",tt,{passive:!0}),t.popUp.addEventListener("touchend",tt,{passive:!0})),window.addEventListener("click",et,{passive:!0})}),10)),window.hash!==P&<(t,t.popUp,!0))}function lt(t,n,i){if(!Z||R)if((i||R&&k!==P+!0)&&!e){const e=n.querySelectorAll("frigate-card, hui-picture-glance-card, hui-picture-entity-card, hui-picture-elements-card, hui-picture-card, hui-camera-card, webrtc-camera"+R);for(let n=0;n0;){t.videoPaused=!1;var o=T.shift();o.parent.insertBefore(o.element,o.nextSibling)}}Q&&(t.stateEntityOld&&t.stateEntityOld===Q?t.stateEntityChanged=!1:(t.stateEntityOld=Q,t.stateEntityChanged=!0));const{backdrop:dt,toggleBackdrop:ct}=function(){if(window.backdrop)return window.backdrop;const t=document.createElement("div");t.classList.add("backdrop"),document.body.appendChild(t);const e=document.createElement("style");return e.innerHTML=`\n\t\t .backdrop {\n\t\t\t\tbackground-color: ${c(U,.7,.7)};\n\t\t\t\tposition: fixed;\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 0;\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 100%;\n\t\t\t\tz-index: 0;\n\t\t\t\topacity: 0;\n\t\t\t\ttransition: opacity 0.3s;\n\t\t\t\tdisplay: flex;\n\t\t\t\tpointer-events: none;\n\t\t }\n\n\t\t .backdrop.visible {\n\t\t\t\topacity: 1;\n\t\t\t\tbackdrop-filter: blur(16px);\n\t\t\t\t-webkit-backdrop-filter: blur(16px);\n\t\t }\n\n\t\t .backdrop.hidden {\n\t\t\t\topacity: 0;\n\t\t\t\tbackdrop-filter: none;\n\t\t\t\t-webkit-backdrop-filter: none;\n\t\t }\n\t\t `,document.head.appendChild(e),window.backdrop={backdrop:t,toggleBackdrop:function(){N||(1===window.openPopups&&(t.classList.add("visible"),t.classList.remove("hidden")),0===window.openPopups&&(t.classList.add("hidden"),t.classList.remove("visible")))}},window.backdrop}();function pt(){let o=t.popUp;w=W?n.formatEntityState(n.states[W]):"",t.headerAdded?(_||t.stateEntityChanged)&&(t.iconContainer.innerHTML="",r(t,d,h,t.iconContainer,e),t.h2.textContent=m,t.p.textContent=w+" "+Y,t.haIcon2.setAttribute("style",`display: ${F};`)):(t.headerContainer=document.createElement("div"),t.headerContainer.setAttribute("id","header-container"),t.div=document.createElement("div"),t.headerContainer.appendChild(t.div),t.iconContainer=document.createElement("div"),t.iconContainer.setAttribute("class","icon-container"),t.div.appendChild(t.iconContainer),r(t,d,h,t.iconContainer,e),E(t.iconContainer,i),t.h2=document.createElement("h2"),t.h2.textContent=m,t.div.appendChild(t.h2),t.p=document.createElement("p"),t.p.textContent=w+" "+Y,t.div.appendChild(t.p),t.haIcon2=document.createElement("ha-icon"),t.haIcon2.setAttribute("class","power-button"),t.haIcon2.setAttribute("icon","mdi:power"),t.haIcon2.setAttribute("style",`display: ${F};`),t.div.appendChild(t.haIcon2),t.button=document.createElement("button"),t.button.setAttribute("class","close-pop-up"),t.button.onclick=function(){tt(),localStorage.setItem("isManuallyClosed_"+P,!0)},t.headerContainer.appendChild(t.button),t.haIcon3=document.createElement("ha-icon"),t.haIcon3.setAttribute("icon","mdi:close"),t.button.appendChild(t.haIcon3),t.content.appendChild(t.headerContainer),t.header=t.div,t.headerAdded=!0),function(){if(d){const e=n.states[d].attributes.rgb_color;t.rgbColor=e?l(e)?"rgb(255,220,200)":`rgb(${e})`:y?d.startsWith("light.")?"rgba(255,220,200, 0.5)":"var(--accent-color)":"rgba(255, 255, 255, 1",t.rgbColorOpacity=e?l(e)?"rgba(255,220,200, 0.5)":`rgba(${e}, 0.5)`:d&&y?d.startsWith("light.")?"rgba(255,220,200, 0.5)":"var(--accent-color)":"var(--background-color,var(--secondary-background-color))",O||(O=c(B,0)),t.iconFilter=e?l(e)?"none":"brightness(1.1)":"none"}else O||(O=c(B,0))}(),t.eventAdded||e?t.eventAdded&&e&&(lt(t,t.popUp,!1),window.removeEventListener("urlChanged",window["checkHashRef_"+P]),ct(),t.eventAdded=!1):(lt(t,t.popUp,!0),window["checkHashRef_"+P]=st,window.addEventListener("urlChanged",window["checkHashRef_"+P],{passive:!0}),t.eventAdded=!0);const p=` \n ha-card {\n margin-top: 0 !important;\n background: none !important;\n border: none !important;\n }\n .pop-up.card-content {\n width: 100% !important;\n padding: 0 !important;\n }\n .pop-up {\n \ttransition: transform .36s;\n position: fixed;\n margin: 0 -${x}; /* 7px */\n width: 100%;\n background-color: ${C};\n box-shadow: 0px 0px 50px rgba(0,0,0,${A/100});\n backdrop-filter: ${N||window.hideBackdrop?"blur("+I+"px)":"none"};\n -webkit-backdrop-filter: ${N||window.hideBackdrop?"blur("+I+"px)":"none"};\n border-radius: 42px;\n box-sizing: border-box;\n top: calc(120% + ${J} + var(--header-height));\n grid-gap: 12px;\n gap: 12px;\n grid-auto-rows: min-content;\n padding: 18px 18px 220px 18px;\n height: 100%;\n -ms-overflow-style: none; /* for Internet Explorer, Edge */\n scrollbar-width: none; /* for Firefox */\n overflow-y: auto; \n overflow-x: hidden; \n z-index: 1 !important; /* Higher value hide the more-info panel */\n /* For older Safari but not working with Firefox */\n /* display: grid !important; */ \n }\n .pop-up > :first-child::after {\n content: '';\n display: block;\n position: sticky;\n top: 0;\n left: -50px;\n margin: -70px 0 -36px -36px;\n overflow: visible;\n width: 200%;\n height: 100px;\n background: linear-gradient(0deg, ${O} 0%, ${C} 80%);\n z-index: 0;\n } \n .pop-up::-webkit-scrollbar {\n display: none; /* for Chrome, Safari, and Opera */\n }\n .pop-up > :first-child {\n position: sticky;\n top: 0;\n z-index: 1;\n background: none !important;\n overflow: visible;\n }\n .pop-up.open-pop-up {\n transform: translateY(-120%);\n }\n .pop-up.close-pop-up { \n transform: translateY(-20%);\n box-shadow: none !important;\n }\n @media only screen and (min-width: 600px) {\n .pop-up {\n top: calc(120% + ${K} + var(--header-height));\n width: calc(${b}${"%"!==f[2]||v?"":" - var(--mdc-drawer-width)"}) !important;\n left: calc(50% - ${f[1]/2}${f[2]});\n margin: 0 !important;\n }\n } \n @media only screen and (min-width: 870px) {\n .pop-up {\n left: calc(50% - ${f[1]/2}${f[2]} + ${v?"0px":"var(--mdc-drawer-width) "+("%"===f[2]?"":"/ 2")});\n }\n } \n .pop-up.editor {\n position: inherit !important;\n width: 100% !important;\n padding: 18px !important;\n backdrop-filter: none !important;\n }\n `,u=`\n ${D}\n\n ha-card {\n margin-top: 0 !important;\n }\n #header-container {\n display: inline-flex;\n ${h||m||d||Q||Y?"":"flex-direction: row-reverse;"}\n height: 50px;\n width: 100%;\n margin: 0;\n padding: 0;\n }\n #header-container > div {\n display: ${h||m||d||Q||Y?"inline-flex":"none"};\n align-items: center;\n position: relative;\n padding-right: 6px;\n z-index: 1;\n flex-grow: 1;\n background-color: ${d?t.rgbColorOpacity:"var(--background-color,var(--secondary-background-color))"};\n transition: background 1s;\n border-radius: 25px;\n margin-right: 14px;\n backdrop-filter: blur(14px);\n -webkit-backdrop-filter: blur(14px);\n }\n #header-container h2 {\n display: inline-flex;\n margin: 0 18px 0 0;\n padding: 4px;\n z-index: 1;\n font-size: 18px;\n }\n #header-container p {\n display: inline-flex;\n font-size: 16px;\n min-width: fit-content ;\n }\n .power-button {\n cursor: pointer; \n flex-grow: inherit; \n width: 24px;\n height: 24px;\n border-radius: 12px;\n margin: 0 10px;\n background: none !important;\n justify-content: flex-end;\n background-color: var(--background-color,var(--secondary-background-color));\n }\n .close-pop-up {\n height: 50px;\n width: 50px;\n border: none;\n border-radius: 50%;\n z-index: 1;\n background: var(--background-color,var(--secondary-background-color));\n color: var(--primary-text-color);\n flex-shrink: 0;\n cursor: pointer;\n }\n `;a(n,t,p,s,Q,d,"","",o),a(n,t,u,s,Q,d,_)}let ht=i.trigger_entity?i.trigger_entity:"",ut=i.trigger_state?i.trigger_state:"",gt=!!i.trigger_close&&i.trigger_close;t.popUp&&ht&&function(e,n){if(!n||n===e)return;e=n,null===localStorage.getItem("previousTriggerState_"+P)&&localStorage.setItem("previousTriggerState_"+P,""),null===localStorage.getItem("isManuallyClosed_"+P)&&localStorage.setItem("isManuallyClosed_"+P,"false"),null===localStorage.getItem("isTriggered_"+P)&&localStorage.setItem("isTriggered_"+P,"false");let i=localStorage.getItem("previousTriggerState_"+P),o="true"===localStorage.getItem("isManuallyClosed_"+P),a="true"===localStorage.getItem("isTriggered_"+P);n!==ut||null!==i||a||(u(0,P),a=!0,localStorage.setItem("isTriggered_"+P,a)),n!==i&&(o=!1,localStorage.setItem("previousTriggerState_"+P,n),localStorage.setItem("isManuallyClosed_"+P,o)),n!==ut||o?n!==ut&>&&t.popUp.classList.contains("open-pop-up")&&a&&!o&&(tt(),a=!1,o=!0,localStorage.setItem("isManuallyClosed_"+P,o),localStorage.setItem("isTriggered_"+P,a)):(u(0,P),a=!0,localStorage.setItem("isTriggered_"+P,a))}(void 0,n.states[ht].state)}(this);break;case"button":!function(t){const e=t._hass,n=t.editor;let{customStyles:i,entityId:o,icon:s,name:l,state:d,stateChanged:c,stateOn:p,formatedState:u,rgbaColor:m,rgbColor:b,iconColorOpacity:f,iconColor:v,iconFilter:_,iconStyles:y}=L(t,t.config,e);u=o&&(c||n)?e.formatEntityState(e.states[o]):"";let w=t.config.button_type||"switch",x=!!t.config.show_state&&t.config.show_state,k=o?e.states[o].attributes.brightness||0:"",C=o?e.states[o].attributes.volume_level||0:"",$=!1,S=k,O=C,A=0,T=0,I=0,V=!1,z=null;if(t.config.service_on,t.config.service_off,!t.buttonAdded){const e=document.createElement("div");e.setAttribute("class","button-container"),t.content.appendChild(e)}const M=document.createElement("div");M.setAttribute("class","icon-container"),t.iconContainer=M;const D=document.createElement("div");D.setAttribute("class","name-container");const U=document.createElement("div");U.setAttribute("class","switch-button");const B=document.createElement("div");B.setAttribute("class","range-slider");const H=document.createElement("div");if(H.setAttribute("class","range-fill"),!t.buttonContainer||n){if(n&&t.buttonContainer){for(;t.buttonContainer.firstChild;)t.buttonContainer.removeChild(t.buttonContainer.firstChild);t.eventAdded=!1,t.wasEditing=!0}t.buttonContainer=t.content.querySelector(".button-container"),"slider"!==w||t.buttonAdded&&!n?("switch"===w||"custom"===w||n)&&(t.buttonContainer.appendChild(U),U.appendChild(M),U.appendChild(D),t.switchButton=t.content.querySelector(".switch-button")):(t.buttonContainer.appendChild(B),B.appendChild(M),B.appendChild(D),B.appendChild(H),t.rangeFill=t.content.querySelector(".range-fill")),r(t,o,s,M,n),D.innerHTML=`\n

${l}

\n ${x?`

${u}

`:""}\n `,t.buttonAdded=!0}function P(t){h("success");let e=t.querySelector(".feedback-element");e||(e=document.createElement("div"),e.setAttribute("class","feedback-element"),t.appendChild(e)),e.style.animation="tap-feedback .5s",setTimeout((()=>{e.style.animation="none",t.removeChild(e)}),500)}function F(t){A=t.pageX||(t.touches?t.touches[0].pageX:0),T=t.pageY||(t.touches?t.touches[0].pageY:0),I=B.value,t.target!==M&&t.target!==M.querySelector("ha-icon")&&($=!0,document.addEventListener("mouseup",W,{passive:!0}),document.addEventListener("touchend",W,{passive:!0}),document.addEventListener("mousemove",Y,{passive:!0}),document.addEventListener("touchmove",Y,{passive:!0}),z=setTimeout((()=>{R(t.pageX||t.touches[0].pageX),q(),z=null}),200))}function Y(t){const e=t.pageX||(t.touches?t.touches[0].pageX:0),n=t.pageY||(t.touches?t.touches[0].pageY:0);Math.abs(n-T)>Math.abs(e-A)?(clearTimeout(z),W()):(document.removeEventListener("mousemove",Y),document.removeEventListener("touchmove",Y),document.addEventListener("mousemove",N,{passive:!0}),document.addEventListener("touchmove",N,{passive:!0}))}function W(){$=!1,V=!1,q(),document.removeEventListener("mouseup",W),document.removeEventListener("touchend",W),document.removeEventListener("mousemove",N),document.removeEventListener("touchmove",N)}function q(){o.startsWith("light.")?(k=S,e.callService("light","turn_on",{entity_id:o,brightness:k})):o.startsWith("media_player.")&&(C=O,e.callService("media_player","volume_set",{entity_id:o,volume_level:C}))}function N(t){const e=t.pageX||(t.touches?t.touches[0].pageX:0),n=t.pageY||(t.touches?t.touches[0].pageY:0);$&&Math.abs(e-A)>10?(h("light"),R(e)):$&&Math.abs(n-T)>10&&($=!1,B.value=I)}function R(t){const e=B.getBoundingClientRect(),n=Math.min(Math.max(t-e.left,0),e.width)/e.width;o.startsWith("light.")?S=Math.round(255*n):o.startsWith("media_player.")&&(O=n),H.style.transition="none",H.style.transform=`translateX(${100*n}%)`}x&&u&&(t.content.querySelector(".state").textContent=u),t.eventAdded||"switch"!==w?t.eventAdded||"slider"!==w?t.eventAdded||"custom"!==w||(U.addEventListener("click",(()=>P(t.switchButton)),{passive:!0}),E(U,t.config),t.eventAdded=!0):(B.addEventListener("mousedown",F,{passive:!0}),B.addEventListener("touchstart",F,{passive:!0}),E(M,t.config),t.eventAdded=!0):(U.addEventListener("click",(()=>P(t.switchButton)),{passive:!0}),U.addEventListener("click",(function(t){t.target!==M&&t.target!==M.querySelector("ha-icon")&&g(e,o)}),{passive:!0}),E(M,t.config),t.eventAdded=!0),t.isDragging||"slider"!==w||(t.rangeFill.style.transition="all .3s",o.startsWith("light.")?t.rangeFill.style.transform=`translateX(${k/255*100}%)`:o.startsWith("media_player.")&&(t.rangeFill.style.transform=`translateX(${100*C}%)`));const j=`\n ha-card {\n margin-top: 0 !important;\n background: none !important;\n opacity: ${"unavailable"!==d?"1":"0.5"};\n }\n \n .button-container {\n position: relative;\n width: 100%;\n height: 50px;\n z-index: 0;\n background-color: var(--background-color-2,var(--secondary-background-color));\n border-radius: 25px;\n mask-image: radial-gradient(white, black);\n -webkit-mask-image: radial-gradient(white, black);\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n -webkit-transform: translateZ(0);\n overflow: hidden;\n }\n \n .switch-button,\n .range-slider {\n display: inline-flex;\n position: absolute;\n height: 100%;\n width: 100%;\n transition: background-color 1.5s;\n background-color: ${p&&["switch","custom"].includes(w)?"var(--accent-color)":"rgba(0,0,0,0)"};\n }\n\n .range-fill {\n z-index: -1;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n background-color: ${f};\n width: 100%;\n left: -100%;\n }\n \n .switch-button {\n cursor: pointer !important;\n }\n \n .range-slider {\n cursor: ew-resize;\n }\n \n .name-container {\n position: relative;\n display: ${x?"block":"inline-flex"};\n margin-left: 4px;\n z-index: 1;\n font-weight: 600;\n align-items: center;\n line-height: ${x?"4px":"16px"};\n padding-right: 16px;\n }\n \n .state {\n font-size: 12px;\n opacity: 0.7;\n }\n \n .feedback-element {\n position: absolute;\n top: 0;\n left: 0;\n opacity: 0;\n width: 100%;\n height: 100%;\n background-color: rgb(0,0,0);\n }\n \n @keyframes tap-feedback {\n 0% {transform: translateX(-100%); opacity: 0;}\n 64% {transform: translateX(0); opacity: 0.1;}\n 100% {transform: translateX(100%); opacity: 0;}\n }\n\n ${y}\n `;a(e,t,j,i,d,o,c)}(this);break;case"separator":!function(t){const e=t._hass,n=t.editor,i=t.config;let{customStyles:o,icon:r,name:s}=L(t,i,e);if(!t.separatorAdded||n){if(n&&t.separatorContainer)for(;t.separatorContainer.firstChild;)t.separatorContainer.removeChild(t.separatorContainer.firstChild);t.separatorAdded||(t.separatorContainer=document.createElement("div"),t.separatorContainer.setAttribute("class","separator-container")),t.separatorContainer.innerHTML=`\n
\n \n

${s}

\n
\n
\n `,t.content.appendChild(t.separatorContainer),t.separatorAdded=!0}a(e,t,"\n .separator-container {\n display: inline-flex;\n width: 100%;\n margin-top: 12px;\n }\n .separator-container div:first-child {\n display: inline-flex;\n max-width: calc(100% - 38px);\n }\n .separator-container div ha-icon {\n display: inline-flex;\n height: 24px;\n width: 24px;\n margin: 0 22px 0 8px;\n transform: translateY(-2px);\n }\n .separator-container div h4 {\n display: inline-flex;\n margin: 0 20px 0 0;\n font-size: 16px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .separator-container div:last-child {\n display: inline-flex; \n border-radius: 6px; \n opacity: 0.5; \n margin-left: 10px; \n flex-grow: 1; \n height: 6px; \n align-self: center; \n background-color: var(--background-color,var(--secondary-background-color));\n }\n ",o)}(this);break;case"cover":!function(t){const e=t._hass,n=t.editor,i=t.config;let{customStyles:o,entityId:r,icon:s,name:l,state:d,stateChanged:c,stateOn:p,formatedState:h,iconStyles:u}=L(t,i,e);const g=i.icon_open?i.icon_open:"mdi:window-shutter-open",m=i.icon_close?i.icon_close:"mdi:window-shutter",b=i.open_service?i.open_service:"cover.open_cover",f=i.close_service?i.close_service:"cover.close_cover",v=i.stop_service?i.stop_service:"cover.stop_cover",_=i.icon_up?i.icon_up:"mdi:arrow-up",y=i.icon_down?i.icon_down:"mdi:arrow-down",w=!!t.config.show_state&&t.config.show_state;if(s="open"===e.states[i.entity].state?g:m,h=c?e.formatEntityState(e.states[r]):h||"",!t.coverAdded||n){if(n&&t.coverContainer)for(;t.coverContainer.firstChild;)t.coverContainer.removeChild(t.coverContainer.firstChild);t.coverContainer=document.createElement("div"),t.coverContainer.setAttribute("class","cover-container"),t.coverContainer.innerHTML=`\n
\n
\n
\n
\n

${l}

\n

\n
\n
\n
\n \n \n \n
\n `,t.content.appendChild(t.coverContainer);const o=t.coverContainer.querySelector(".open"),a=t.coverContainer.querySelector(".stop"),s=t.coverContainer.querySelector(".close");o.addEventListener("click",(()=>{e.callService(b.split(".")[0],b.split(".")[1],{entity_id:r})}),{passive:!0}),a.addEventListener("click",(()=>{e.callService(v.split(".")[0],v.split(".")[1],{entity_id:r})}),{passive:!0}),s.addEventListener("click",(()=>{e.callService(f.split(".")[0],f.split(".")[1],{entity_id:r})}),{passive:!0}),t.iconContainer=t.content.querySelector(".icon-container"),E(t.iconContainer,i),t.coverAdded=!0}t.iconContainer&&(c||n)&&(t.iconContainer.innerHTML=``,t.content.querySelector(".state").textContent=w?h:""),a(e,t,"\n ha-card {\n margin-top: 0 !important;\n background: none !important;\n }\n \n .header-container {\n display: flex;\n align-items: center;\n margin-bottom: 10px;\n }\n \n .cover-container {\n display: grid;\n }\n \n .icon-container {\n display: flex;\n margin: 0 !important;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n /*z-index: 1;*/\n width: 48px;\n height: 48px;\n margin: 6px;\n border-radius: 50%;\n background-color: var(--card-background-color,var(--ha-card-background));\n border: 6px solid var(--background-color-2,var(--secondary-background-color));\n box-sizing: border-box;\n }\n \n .name-container {\n font-weight: 600;\n margin-left: 10px;\n line-height: 4px;\n }\n \n .buttons-container {\n display: grid;\n align-self: center;\n grid-auto-flow: column;\n grid-gap: 18px; \n }\n \n .state {\n font-size: 12px;\n opacity: 0.7;\n }\n \n ha-icon {\n display: flex; \n height: 24px; \n width: 24px; \n color: var(--primary-text-color);\n }\n \n .button {\n display: flex;\n background: var(--background-color-2,var(--secondary-background-color));\n height: 42px;\n border-radius: 32px;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border: none;\n }\n ",o,d,r)}(this);break;case"empty-column":!function(t){if(!t.emptyCollumnAdded){const e=document.createElement("div");e.setAttribute("class","empty-column"),e.innerHTML='\n
\n ',t.content.appendChild(e),t.emptyColumnAdded=!0}}(this);break;case"horizontal-buttons-stack":I(this)}!async function(t){if(!window.resourcesChecked){window.resourcesChecked=!0;try{let e=(await t.callWS({type:"lovelace/resources"})).find((t=>t.url.includes("bubble-pop-up.js")));async function n(t){let e=await t.callWS({type:"lovelace/resources"}),n=e.findIndex((t=>t.url.includes("bubble-card.js"))),i=null;if(-1!==n&&0!==n){i=e.splice(n,1)[0];for(let n of e)await t.callWS({type:"lovelace/resources/delete",resource_id:n.id});i&&-1===(await t.callWS({type:"lovelace/resources"})).findIndex((t=>t.url.includes("bubble-card.js")))&&await t.callWS({type:"lovelace/resources/create",res_type:i.type,url:i.url});for(let n of e)await t.callWS({type:"lovelace/resources/create",res_type:n.type,url:n.url})}}e&&await t.callWS({type:"lovelace/resources/delete",resource_id:e.id}),n(t)}catch(i){throw i}}}(t),window.columnFix||(window.columnFix=this.config.column_fix)}setConfig(t){if("pop-up"===t.card_type){if(!t.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("horizontal-buttons-stack"===t.card_type){var e={};for(var n in t)if(n.match(/^\d+_icon$/)){var i=n.replace("_icon","_link");if(void 0===t[i])throw new Error("You need to define "+i);if(e[t[i]])throw new Error("You can't use "+t[i]+" twice");e[t[i]]=!0}}else if(("button"===t.card_type||"cover"===t.card_type||"state"===t.card_type)&&!t.entity)throw new Error("You need to define an entity");if(window.entityError)throw new Error("You need to define a valid entity");this.config=t}getCardSize(){return"true"===window.columnFix?0:"number"==typeof window.columnFix?window.columnFix:-10}static getConfigElement(){return document.createElement("bubble-card-editor")}}customElements.define("bubble-card",U),customElements.define("bubble-card-editor",class extends V{setConfig(t){this._config={...t}}static get properties(){return{hass:{},_config:{}}}get _card_type(){return this._config.card_type||""}get _button_type(){return this._config.button_type||"switch"}get _entity(){return this._config.entity||""}get _name(){return this._config.name||""}get _icon(){return this._config.icon||""}get _state(){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||!1}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 void 0!==this._config.bg_opacity?this._config.bg_opacity:"88"}get _bg_blur(){return void 0!==this._config.bg_blur?this._config.bg_blur:"14"}get _shadow_opacity(){return void 0!==this._config.shadow_opacity?this._config.shadow_opacity:"0"}get _is_sidebar_hidden(){return this._config.is_sidebar_hidden||!1}get _rise_animation(){return void 0===this._config.rise_animation||this._config.rise_animation}get _auto_close(){return this._config.auto_close||""}get _close_on_click(){return this._config.close_on_click||!1}get _background_camera(){return this._config.background_camera||!1}get _icon_open(){return this._config.icon_open||""}get _icon_close(){return this._config.icon_close||""}get _open_service(){return this._config.open_service||"cover.open_cover"}get _close_service(){return this._config.open_service||"cover.close_cover"}get _stop_service(){return this._config.open_service||"cover.stop_cover"}get _auto_order(){return this._config.auto_order||!1}get _highlight_current_view(){return this._config.highlight_current_view||!1}get _show_state(){return this._config.show_state||!1}get _hide_backdrop(){return this._config.hide_backdrop||!1}get _hide_gradient(){return this._config.hide_gradient||!1}render(){if(!this.hass)return z``;if(!this.listsUpdated){const t=t=>({label:t,value:t});this.allEntitiesList=Object.keys(this.hass.states).map(t),this.lightList=Object.keys(this.hass.states).filter((t=>"light"===t.substr(0,t.indexOf(".")))).map(t),this.sensorList=Object.keys(this.hass.states).filter((t=>"sensor"===t.substr(0,t.indexOf(".")))).map(t),this.binarySensorList=Object.keys(this.hass.states).filter((t=>"binary_sensor"===t.substr(0,t.indexOf(".")))).map(t),this.coverList=Object.keys(this.hass.states).filter((t=>"cover"===t.substr(0,t.indexOf(".")))).map(t),this.cardTypeList=[{label:"Button",value:"button"},{label:"Cover",value:"cover"},{label:"Empty column",value:"empty-column"},{label:"Horizontal buttons stack",value:"horizontal-buttons-stack"},{label:"Pop-up",value:"pop-up"},{label:"Separator",value:"separator"}],this.buttonTypeList=[{label:"Switch",value:"switch"},{label:"Slider",value:"slider"}],this.listsUpdated=!0}const e=this.allEntitiesList,n=(this.lightList,this.sensorList,this.coverList),i=this.cardTypeList,o=this.buttonTypeList;if("pop-up"===this._config.card_type)return z`
- ${this.makeDropdown("Card type","card_type",o)} -

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("Card type","card_type",i)} +

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.
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.
+ Since v1.7.0, the optimized mode has been removed to ensure stability and to simplify updates for everyone. However, if your pop-up content still appears on the screen during page loading, you can install this similar fix. ${this.makeDropdown("Optional - Icon","icon")} - ${this.makeDropdown("Optional - Entity to toggle (e.g. room light group)","entity",t)} - ${this.makeDropdown("Optional - Entity state to display (e.g. room temperature)","state",t)} + ${this.makeDropdown("Optional - Entity to toggle (e.g. room light group)","entity",e)} + ${this.makeDropdown("Optional - Entity state to display (e.g. room temperature)","state",e)} Optional - Close the pop-up after any click or tap
+ + +
+ +
+

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",t)} + ${this.makeDropdown("Optional - Entity to open the pop-up based on its state","trigger_entity",e)} - Set ‘Background blur’ to 0 if your pop-up animations are rendering at low FPS. + + +
+ +
+
+ Set this toggle to true on the first pop-up of your main dashboard to disable the backdrop on all pop-ups. ${this.makeVersion()} - `;if("button"===this._config.card_type)return C` + `;if("button"===this._config.card_type)return z`
- ${this.makeDropdown("Card type","card_type",o)} + ${this.makeDropdown("Card type","card_type",i)}

Button

This card can be a slider or a button, allowing you to toggle your entities, control the brightness of your lights and the volume of your media players. To access color / control of an entity, simply tap on the icon. - ${this.makeDropdown("slider"!==this._button_type?"Entity (toggle)":"Entity (light or media_player)","entity",t)} + ${this.makeDropdown("slider"!==this._button_type?"Entity (toggle)":"Entity (light or media_player)","entity",e)} Optional - Show entity state
- ${this.makeDropdown("Button type","button_type",i)} + ${this.makeDropdown("Button type","button_type",o)} - `;if("separator"===this._config.card_type)return C` + `;if("separator"===this._config.card_type)return z`
- ${this.makeDropdown("Card type","card_type",o)} + ${this.makeDropdown("Card type","card_type",i)}

Separator

This card is a simple separator for dividing your pop-up into categories / sections. e.g. Lights, Devices, Covers, Settings, Automations... - `;if("horizontal-buttons-stack"===this._config.card_type){if(!this.buttonAdded&&this.shadowRoot.querySelector("#add-button")){this.buttonAdded=!0;const e=this.shadowRoot.querySelector("#add-button");for(this.buttonIndex=0;this._config[this.buttonIndex+1+"_link"];)this.buttonIndex++;e.addEventListener("click",(()=>{this.buttonIndex++;const t=e.style.opacity,n=e.innerText;e.style.opacity="0.6",e.style.transition="opacity 1s",e.innerText="Loading...",setTimeout((()=>{e.style.opacity=t,e.innerText=n}),5e3)}),{passive:!0})}return C` + `;if("horizontal-buttons-stack"===this._config.card_type){if(!this.buttonAdded&&this.shadowRoot.querySelector("#add-button")){this.buttonAdded=!0;const t=this.shadowRoot.querySelector("#add-button");for(this.buttonIndex=0;this._config[this.buttonIndex+1+"_link"];)this.buttonIndex++;t.addEventListener("click",(()=>{this.buttonIndex++;const e=t.style.opacity,n=t.innerText;t.style.opacity="0.6",t.style.transition="opacity 1s",t.innerText="Loading...",setTimeout((()=>{t.style.opacity=e,t.innerText=n}),5e3)}),{passive:!0})}return z`
- ${this.makeDropdown("Card type","card_type",o)} + ${this.makeDropdown("Card type","card_type",i)}

Horizontal buttons stack

This card is the companion to the pop-up card, allowing you to open the corresponding pop-ups. It also allows you to open any page of your dashboard. In addition, you can add your motion sensors so that the order of the buttons adapts according to the room you just entered. This card is scrollable, remains visible and acts as a footer.

Please note that this card may take some time to load in edit mode.
@@ -265,8 +278,8 @@ @@ -274,11 +287,22 @@
+ + +
+ +
+
${this.makeVersion()}
- `}return"cover"===this._config.card_type?C` + `}return"cover"===this._config.card_type?z`
- ${this.makeDropdown("Card type","card_type",o)} + ${this.makeDropdown("Card type","card_type",i)}

Cover

This card allows you to control your covers. ${this.makeDropdown("Entity","entity",n)} @@ -328,20 +352,20 @@ ${this.makeDropdown("Optional - Arrow up icon","icon_up")} ${this.makeVersion()}
- `:"empty-column"===this._config.card_type?C` + `:"empty-column"===this._config.card_type?z`
- ${this.makeDropdown("Card type","card_type",o)} + ${this.makeDropdown("Card type","card_type",i)}

Empty column

Just an empty card to fill any empty column. ${this.makeVersion()}
- `:this._config.card_type?void 0:C` + `:this._config.card_type?void 0:z`
- ${this.makeDropdown("Card type","card_type",o)} + ${this.makeDropdown("Card type","card_type",i)} You need to add a card type first. -

The Bubble Card ${e} changelog is available here. -

Column fix: If you experience some issues with your dashboard layout, such as empty columns or misaligned cards. You can apply a fix that restores the behavior of the previous versions by adding column_fix: true in YAML to the first Bubble Card on your dashboard. Then refresh the page. +

The Bubble Card ${t} changelog is available here. +

Column fix: If you experience some issues with your dashboard layout, such as empty columns or misaligned cards. You can apply a fix that restores the behavior of the previous versions by adding column_fix: true in YAML to the first Bubble Card on your dashboard. You can also try to add a negative value to find the one that fit your dashboard like column_fix: -10. Then refresh the page.


Almost everything is available in the GUI editor, but in the YAML editor you can add your own custom styles, create custom buttons or modify the tap actions of all cards. You can find more details on my GitHub page.

@@ -353,72 +377,72 @@
${this.makeVersion()} - `}makeDropdown(e,t,n){return this.hass,e.includes("icon")||e.includes("Icon")?C` + `}makeDropdown(t,e,n){return this.hass,t.includes("icon")||t.includes("Icon")?z`
- `:C` + `:z`
- `}makeButton(){let e=[];for(let t=1;t<=this.buttonIndex;t++)e.push(C` -
+ `}makeButton(){let t=[];for(let e=1;e<=this.buttonIndex;e++)t.push(z` +
- this.removeButton(t)}> - Button ${t} + this.removeButton(e)}> + Button ${e}
- `);return e}makeVersion(){return C` + `);return t}makeVersion(){return z`

- ${e} + ${t}

- `}removeButton(e){delete this._config[e+"_name"],delete this._config[e+"_icon"],delete this._config[e+"_link"],delete this._config[e+"_entity"],delete this._config[e+"_pir_sensor"];for(let t=e;t { @@ -62,14 +46,22 @@ export function handleHorizontalButtonsStack(context) { } }, 100); + let hideGradient = context.config.hide_gradient ? true : false; + const createButton = (button, link, icon) => { - const buttonElement = document.createElement("button"); + const buttonElement = document.createElement("div"); buttonElement.setAttribute("class", `button ${link.substring(1)}`); buttonElement.innerHTML = ` ${icon !== '' ? `` : ''} ${button !== '' ? `

${button}

` : ''} +
`; + const backgroundElement = document.createElement("div"); + backgroundElement.setAttribute("class", "color-background"); + buttonElement.appendChild(backgroundElement); + buttonElement.background = backgroundElement; + const handleClick = (event) => { popUpOpen = location.hash + true; if (popUpOpen !== link + true) { @@ -114,18 +106,19 @@ export function handleHorizontalButtonsStack(context) { context.buttonsContainer = buttonsContainer; } - const updateButtonStyle = (buttonElement, lightEntity, buttonLink) => { + const updateButtonStyle = (buttonElement, lightEntity) => { + const backgroundElement = buttonElement.background; if (hass.states[lightEntity].attributes.rgb_color) { const rgbColor = hass.states[lightEntity].attributes.rgb_color; const rgbColorOpacity = (!isColorCloseToWhite(rgbColor) ? `rgba(${rgbColor}, 0.5)` : 'rgba(255,220,200, 0.5)') - buttonElement.style.backgroundColor = rgbColorOpacity; - buttonElement.style.border = '1px solid rgba(0,0,0,0)'; + backgroundElement.style.backgroundColor = rgbColorOpacity; + backgroundElement.style.border = '1px solid rgba(0,0,0,0)'; } else if (!hass.states[lightEntity].attributes.rgb_color && hass.states[lightEntity].state == 'on') { - buttonElement.style.backgroundColor = 'rgba(255,255,255,0.5)'; - buttonElement.style.border = '1px solid rgba(0,0,0,0)'; + backgroundElement.style.backgroundColor = 'rgba(255,255,255,0.5)'; + backgroundElement.style.border = '1px solid rgba(0,0,0,0)'; } else { - buttonElement.style.backgroundColor = 'rgba(0,0,0,0)'; - buttonElement.style.border = '1px solid var(--primary-text-color)'; + backgroundElement.style.backgroundColor = 'rgba(0,0,0,0)'; + backgroundElement.style.border = '1px solid var(--primary-text-color)'; } }; @@ -203,15 +196,11 @@ export function handleHorizontalButtonsStack(context) { context.buttonsAdded = true; context.buttons = buttons; } - + let currentPosition = 0; let buttonMargin = 12; function updateButtons(context) { - if (context.buttonsUpdated && !editor) { - return; - } - let promises = []; for (let button of buttonsList) { let buttonElement = context.buttons[button.link]; @@ -221,6 +210,10 @@ export function handleHorizontalButtonsStack(context) { } } + if (!context.previousConfig) { + context.previousConfig = context.config; + } + Promise.all(promises).then(results => { let index = 0; for (let button of buttonsList) { @@ -229,25 +222,32 @@ export function handleHorizontalButtonsStack(context) { let buttonWidth = results[index]; let buttonContent = results[index + 1]; index += 2; - if (!buttonWidth || buttonWidth === '0' || buttonContent !== buttonElement.innerHTML || editor) { + if (!buttonWidth || buttonWidth === '0' || editor) { buttonWidth = buttonElement.offsetWidth; localStorage.setItem(`buttonWidth-${button.link}`, buttonWidth); localStorage.setItem(`buttonContent-${button.link}`, buttonElement.innerHTML); context.previousConfig = context.config; } - buttonElement.style.transform = `translateX(${currentPosition}px)`; + + if (currentPosition !== buttonElement.previousPosition) { + buttonElement.style.transform = `translateX(${currentPosition}px)`; + buttonElement.previousPosition = currentPosition; + } currentPosition += parseInt(buttonWidth) + buttonMargin; + } if (button.lightEntity) { updateButtonStyle(buttonElement, button.lightEntity, button.link); } } - - context.buttonsAdded = true; }); } + - updateButtons(context); + if (!context.buttonsUpdated || editor) { + updateButtons(context); + context.buttonsAdded = true; + } const horizontalButtonsStackStyles = ` ha-card { @@ -256,7 +256,7 @@ export function handleHorizontalButtonsStack(context) { .horizontal-buttons-stack { width: 100%; margin-top: 0 !important; - background: none !important; + /*background: none !important;*/ position: fixed; height: 51px; bottom: 16px; @@ -264,10 +264,9 @@ export function handleHorizontalButtonsStack(context) { z-index: 1 !important; /* Higher value hide the more-info panel */ } @keyframes from-bottom { - 0% {transform: translateY(200px);} - 20% {transform: translateY(200px);} - 46% {transform: translateY(-8px);} - 56% {transform: translateY(1px);} + 0% {transform: translateY(100px);} + 26% {transform: translateY(-8px);} + 46% {transform: translateY(1px);} 62% {transform: translateY(-2px);} 70% {transform: translateY(0);} 100% {transform: translateY(0);} @@ -281,7 +280,7 @@ export function handleHorizontalButtonsStack(context) { display: inline-flex; position: absolute; box-sizing: border-box !important; - border: 1px solid var(--primary-text-color); + /*border: 1px solid var(--primary-text-color);*/ align-items: center; height: 50px; line-height: 16px; @@ -290,9 +289,31 @@ export function handleHorizontalButtonsStack(context) { border-radius: 25px; z-index: 1; padding: 0 16px; - background: none; - transition: background-color 1s, border 1s, transform 1s; color: var(--primary-text-color); + transition: background-color 1s, border 1s, transform 1s; + } + .color-background { + border-radius: 24px; + width: 100%; + height: 100%; + box-sizing: border-box !important; + position: absolute; + left: 0; + top: 0; + z-index: -1; + border: 1px solid var(--primary-text-color); + transition: background-color 1s, border 1s, transform 1s; + } + .background { + opacity: 0.8; + border-radius: 24px; + width: 100%; + height: 100%; + box-sizing: border-box !important; + position: absolute; + left: 0; + z-index: -2; + background-color: var(--background-color,var(--primary-background-color)); } .highlight { animation: pulse 1.4s infinite alternate; @@ -317,15 +338,13 @@ export function handleHorizontalButtonsStack(context) { -ms-overflow-style: none; scrollbar-width: none; -webkit-mask-image: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, 1) calc(0% + 28px), rgba(0, 0, 0, 1) calc(100% - 28px), transparent 100%); - /* mask-image: linear-gradient(90deg, transparent 2%, rgba(0, 0, 0, 1) 6%, rgba(0, 0, 0, 1) 96%, transparent 100%); */ - /* -webkit-mask-image: linear-gradient(90deg, transparent 2%, rgba(0, 0, 0, 1) 6%, rgba(0, 0, 0, 1) 96%, transparent 100%); */ } .horizontal-buttons-stack::before { content: ''; position: absolute; top: -32px; left: -100%; - display: block; + display: ${hideGradient ? 'none' : 'block'}; background: linear-gradient(0deg, var(--background-color, var(--primary-background-color)) 50%, rgba(79, 69, 87, 0)); width: 200%; height: 100px; @@ -358,14 +377,11 @@ export function handleHorizontalButtonsStack(context) { overflow: hidden; } .horizontal-buttons-stack.editor::before { - top: -32px; - left: -100%; background: none; - width: 100%; - height: 0; } - .horizontal-buttons-stack-container.editor > .button { - transition: background-color 0s, border 0s, transform 0s; + .horizontal-buttons-stack-container.editor > .button, + .horizontal-buttons-stack-container.editor > .button > .color-background { + transition: background-color 0s, border 0s, transform 0s !important; } .horizontal-buttons-stack-container.editor { margin-left: 1px; @@ -380,7 +396,7 @@ export function handleHorizontalButtonsStack(context) { `; if (!window.hasAnimated && riseAnimation) { - context.content.style.animation = 'from-bottom 1.3s forwards'; + context.content.style.animation = 'from-bottom .6s forwards'; window.hasAnimated = true; setTimeout(() => { context.content.style.animation = 'none'; diff --git a/src/cards/pop-up.ts b/src/cards/pop-up.ts index 87283e5..d5b62b8 100644 --- a/src/cards/pop-up.ts +++ b/src/cards/pop-up.ts @@ -7,11 +7,6 @@ import { getIconColor, getIconStyles } from '../tools/style.ts'; -import { - initializeContent, - checkEditor, - checkResources -} from '../tools/init.ts'; import { fireEvent, forwardHaptic, @@ -23,26 +18,64 @@ import { addActions } from '../tools/tap-actions.ts'; import { getVariables } from '../var/cards.ts'; let oldTriggerEntityState; +let storedElements = []; +let rgbaBgColor; +window.openPopups = 0; export function handlePopUp(context) { - const hass = context._hass; const editor = context.editor; - const config = context.config; - if (!hass) { - return; + // Hide vertical stack content before initialization + + if (!context.initStyleAdded && !context.popUp && !editor) { + context.card.style.marginTop = '4000px'; + context.initStyleAdded = true; } if (context.errorTriggered) { return; } - if (!context.initStyleAdded && !context.popUp && !editor) { - // Hide vertical stack content before initialization - context.card.style.marginTop = '4000px'; - context.initStyleAdded = true; - } + // Initialize/refresh pop-up + + const hass = context._hass; + const config = context.config; + + const initPopUp = setTimeout(() => { + const initEvent = new Event('popUpInitialized'); + + if (!context.verticalStack) { + context.verticalStack = context.getRootNode(); + } else { + clearTimeout(initPopUp); + } + + if (context.verticalStack && ( + !context.popUp + || stateChanged + || context.stateEntityChanged + || (editor && !context.editorModeAdded) + )){ + if (!context.popUp) { + context.popUp = context.verticalStack.querySelector('#root'); + context.popUp.setAttribute("class", "pop-up"); + } + + if (editor && !context.editorModeAdded) { + context.popUp.classList.add('editor'); + context.popUp.classList.remove('close-pop-up', 'open-pop-up'); + context.editorModeAdded = true; + } + + createPopUp(); + window.dispatchEvent(initEvent); + } else if (!editor && context.popUp && context.editorModeAdded) { + context.popUp.classList.remove('editor'); + createPopUp(); + context.editorModeAdded = false; + } + }, 0); let { customStyles, @@ -52,11 +85,9 @@ export function handlePopUp(context) { widthDesktop, widthDesktopDivided, isSidebarHidden, - state, stateChanged, stateOn, formatedState, - riseAnimation, marginCenter, popUpOpen, rgbaColor, @@ -68,31 +99,42 @@ export function handlePopUp(context) { iconColor, iconFilter, iconStyles, - haStyle, themeBgColor, color, } = getVariables(context, config, hass, editor); let autoClose = config.auto_close || false; let popUpHash = config.hash; - let triggerEntity = config.trigger_entity ? config.trigger_entity : ''; - let triggerState = config.trigger_state ? config.trigger_state : ''; - let triggerClose = config.trigger_close ? config.trigger_close : false; let displayPowerButton = config.entity ? 'flex' : 'none'; let text = config.text || ''; let stateEntityId = config.state; let closeOnClick = config.close_on_click || false; + let hideBackdrop = config.hide_backdrop || false; + let hideCard = config.hide_card || ''; + if (!window.hideBackdrop && hideBackdrop) { + window.hideBackdrop = true; + } + let backgroundCamera = context.config.background_camera || false; let marginTopMobile = config.margin_top_mobile ? (config.margin_top_mobile !== '0' ? config.margin_top_mobile : '0px') : '0px'; let marginTopDesktop = config.margin_top_desktop ? (config.margin_top_desktop !== '0' ? config.margin_top_desktop : '0px') : '0px'; - state = stateEntityId && hass.states[stateEntityId] ? hass.states[stateEntityId].state : ''; + let state = stateEntityId && hass.states[stateEntityId] ? hass.states[stateEntityId].state : ''; + + if (state) { + if (!context.stateEntityOld || context.stateEntityOld !== state) { + context.stateEntityOld = state; + context.stateEntityChanged = true; + } else { + context.stateEntityChanged = false; + } + } + let startTouchY; let lastTouchY; let closeTimeout; - let rgbaBgColor; function removeHash() { history.replaceState(null, null, location.href.split('#')[0]); @@ -119,7 +161,7 @@ export function handlePopUp(context) { context.div.appendChild(context.h2); context.p = document.createElement("p"); - context.p.textContent = formatedState; + context.p.textContent = formatedState + ' ' + text; context.div.appendChild(context.p); context.haIcon2 = document.createElement("ha-icon"); @@ -144,30 +186,30 @@ export function handlePopUp(context) { context.header = context.div; context.headerAdded = true; - } else if (entityId) { + } else if (stateChanged || context.stateEntityChanged) { context.iconContainer.innerHTML = ''; // Clear the container createIcon(context, entityId, icon, context.iconContainer, editor); - context.h2.textContent = name; - context.p.textContent = formatedState; + context.p.textContent = formatedState + ' ' + text; context.haIcon2.setAttribute("style", `display: ${displayPowerButton};`); } } - function closePopUpByClickingOutside(e) { + function closePopUpByClickingOutside(element) { // Reset auto close window.hash === popUpHash && resetAutoClose(); - const target = e.composedPath(); + const target = element.composedPath(); if ( target && !target.some(el => el.nodeName === 'HA-MORE-INFO-DIALOG') + && !target.some(el => el.nodeName === 'HA-DIALOG-DATE-PICKER') && !target.some(el => el.id === 'root' && !el.classList.contains('close-pop-up')) ){ const close = setTimeout(function() { - if (window.hash === popUpHash) { //&& !closeOnClick + if (window.hash === popUpHash) { removeHash(); localStorage.setItem('isManuallyClosed_' + popUpHash, true); } @@ -188,8 +230,8 @@ export function handlePopUp(context) { toggleEntity(hass, entityId); } - function windowKeydownHandler(e) { - if (e.key === 'Escape') { + function windowKeydownHandler(event) { + if (event.key === 'Escape') { removeHash(); localStorage.setItem('isManuallyClosed_' + popUpHash, true) } @@ -235,78 +277,161 @@ export function handlePopUp(context) { ? (entityId.startsWith("light.") ? 'rgba(255,220,200, 0.5)' : 'var(--accent-color)') : 'var(--background-color,var(--secondary-background-color))'); - rgbaBgColor = convertToRGBA(color, 0); + if (!rgbaBgColor) { + rgbaBgColor = convertToRGBA(color, 0); + } context.iconFilter = rgbColor ? (!isColorCloseToWhite(rgbColor) ? 'brightness(1.1)' : 'none') : 'none'; - } else { + } else if (!rgbaBgColor) { rgbaBgColor = convertToRGBA(color, 0); } } function checkHash() { - if (!editor) { + if (!editor && context.popUp) { window.hash = location.hash.split('?')[0]; // Open on hash change - if (window.hash === popUpHash && popUpOpen !== popUpHash + true) { + if ( + window.hash === popUpHash && + popUpOpen !== popUpHash + true && + !context.popUp.classList.contains('open-pop-up') + ){ + popUpOpen = popUpHash + true; openPopUp(); // Close on back button from browser - } else if (window.hash !== popUpHash && popUpOpen !== popUpHash + false) { + } else if ( + window.hash !== popUpHash && + popUpOpen !== popUpHash + false && + context.popUp.classList.contains('open-pop-up') + ){ + popUpOpen = popUpHash + false; closePopUp(); } - } - }; - function pauseVideos(root, pause) { - var videos = root.querySelectorAll('video'); - for (var i=0; i 0 && !videos[i].paused && !videos[i].ended && videos[i].readyState > videos[i].HAVE_CURRENT_DATA; - if (pause && isPlaying) { - videos[i].pause(); - } else if (!pause && !isPlaying) { - videos[i].play(); - if (videos[i].currentTime > 0) { - videos[i].currentTime = 10000; - } + if (window.hash !== popUpHash) { + pauseVideos(context, context.popUp, true); } } + }; - var nodes = root.querySelectorAll('*'); - for(var i=0; i 0) { + context.videoPaused = false; + var storedElement = storedElements.shift(); + storedElement.parent.insertBefore(storedElement.element, storedElement.nextSibling); + } + } + } + + function createBackdrop() { + if (window.backdrop) { + return window.backdrop; + } + + const backdrop = document.createElement('div'); + backdrop.classList.add('backdrop'); + + document.body.appendChild(backdrop); + + const style = document.createElement('style'); + style.innerHTML = ` + .backdrop { + background-color: ${convertToRGBA(themeBgColor, 0.7, 0.7)}; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + opacity: 0; + transition: opacity 0.3s; + display: flex; + pointer-events: none; + } + + .backdrop.visible { + opacity: 1; + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + } + + .backdrop.hidden { + opacity: 0; + backdrop-filter: none; + -webkit-backdrop-filter: none; + } + `; + + document.head.appendChild(style); + + function toggleBackdrop() { + if (hideBackdrop) { + return; + } + + if (window.openPopups === 1) { + backdrop.classList.add('visible'); + backdrop.classList.remove('hidden'); + } + + if (window.openPopups === 0) { + backdrop.classList.add('hidden'); + backdrop.classList.remove('visible'); + } + } + + window.backdrop = { + backdrop, + toggleBackdrop, + }; + + return window.backdrop; + } + + const { backdrop, toggleBackdrop } = createBackdrop(); function openPopUp() { - context.popUp.classList.remove('close-pop-up'); - context.popUp.classList.add('open-pop-up'); + window.openPopups++; + context.popUp.classList.remove('close-pop-up'); + context.popUp.classList.add('open-pop-up'); + toggleBackdrop(); context.content.querySelector('.power-button').addEventListener('click', powerButtonClickHandler, { passive: true }); window.addEventListener('keydown', windowKeydownHandler, { passive: true }); context.popUp.addEventListener('touchstart', popUpTouchstartHandler, { passive: true }); context.popUp.addEventListener('touchmove', popUpTouchmoveHandler, { passive: true }); document.body.style.overflow = 'hidden'; // Fix scroll inside pop-ups only - pauseVideos(context.popUp, false); + pauseVideos(context, context.popUp, false); resetAutoClose(); - if (closeOnClick) { - context.popUp.addEventListener('mouseup', removeHash, { passive: true }); - context.popUp.addEventListener('touchend', removeHash, { passive: true }); - } - - popUpOpen = popUpHash + true; - setTimeout(function() { + if (closeOnClick) { + context.popUp.addEventListener('mouseup', removeHash, { passive: true }); + context.popUp.addEventListener('touchend', removeHash, { passive: true }); + } window.addEventListener('click', closePopUpByClickingOutside, { passive: true }); }, 10); } function closePopUp() { + window.openPopups--; context.popUp.classList.remove('open-pop-up'); context.popUp.classList.add('close-pop-up'); + toggleBackdrop(); context.content.querySelector('.power-button').removeEventListener('click', powerButtonClickHandler); window.removeEventListener('keydown', windowKeydownHandler); window.removeEventListener('click', closePopUpByClickingOutside); @@ -320,14 +445,12 @@ export function handlePopUp(context) { context.popUp.removeEventListener('touchend', removeHash); } - popUpOpen = popUpHash + false; - setTimeout(function() { - pauseVideos(context.popUp, true); + pauseVideos(context, context.popUp, true); }, 320); } - function createPopUp() { + function createPopUp() { let popUp = context.popUp; formatedState = stateEntityId ? hass.formatEntityState(hass.states[stateEntityId]) : ''; @@ -335,11 +458,14 @@ export function handlePopUp(context) { updateColor(); if (!context.eventAdded && !editor) { + pauseVideos(context, context.popUp, true); window['checkHashRef_' + popUpHash] = checkHash; window.addEventListener('urlChanged', window['checkHashRef_' + popUpHash], { passive: true }); context.eventAdded = true; } else if (context.eventAdded && editor) { + pauseVideos(context, context.popUp, false); window.removeEventListener('urlChanged', window['checkHashRef_' + popUpHash]); + toggleBackdrop(); context.eventAdded = false; } @@ -349,28 +475,27 @@ export function handlePopUp(context) { background: none !important; border: none !important; } - .card-content { + .pop-up.card-content { width: 100% !important; padding: 0 !important; } - #root { - transition: transform .36s !important; - position: fixed !important; + .pop-up { + transition: transform .36s; + position: fixed; margin: 0 -${marginCenter}; /* 7px */ width: 100%; - ${config.bg_color || config.bg_opacity ? "--bubble-pop-up-background-custom: " + rgbaColor : ''}; - background-color: var(--bubble-pop-up-background-custom, var(--bubble-pop-up-background)); + 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); + backdrop-filter: ${hideBackdrop || window.hideBackdrop ? 'blur(' + bgBlur + 'px)' : 'none'}; + -webkit-backdrop-filter: ${hideBackdrop || window.hideBackdrop ? 'blur(' + bgBlur + 'px)' : 'none'}; border-radius: 42px; box-sizing: border-box; top: calc(120% + ${marginTopMobile} + var(--header-height)); - grid-gap: 12px !important; - gap: 12px !important; + grid-gap: 12px; + gap: 12px; grid-auto-rows: min-content; - padding: 18px 18px 220px 18px !important; - height: 100% !important; + padding: 18px 18px 220px 18px; + height: 100%; -ms-overflow-style: none; /* for Internet Explorer, Edge */ scrollbar-width: none; /* for Firefox */ overflow-y: auto; @@ -379,10 +504,7 @@ export function handlePopUp(context) { /* For older Safari but not working with Firefox */ /* display: grid !important; */ } - #root.hidden { - display: none !important; - } - #root > :first-child::after { + .pop-up > :first-child::after { content: ''; display: block; position: sticky; @@ -395,31 +517,25 @@ export function handlePopUp(context) { background: linear-gradient(0deg, ${rgbaBgColor} 0%, ${rgbaColor} 80%); z-index: 0; } - #root::-webkit-scrollbar { + .pop-up::-webkit-scrollbar { display: none; /* for Chrome, Safari, and Opera */ } - #root > :first-child { + .pop-up > :first-child { position: sticky; top: 0; z-index: 1; background: none !important; overflow: visible; } - #root.open-pop-up { + .pop-up.open-pop-up { transform: translateY(-120%); } - #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 { + .pop-up.close-pop-up { transform: translateY(-20%); - box-shadow: none; + box-shadow: none !important; } @media only screen and (min-width: 600px) { - #root { + .pop-up { 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]}); @@ -427,11 +543,11 @@ export function handlePopUp(context) { } } @media only screen and (min-width: 870px) { - #root { + .pop-up { left: calc(50% - ${widthDesktopDivided[1] / 2}${widthDesktopDivided[2]} + ${isSidebarHidden ? '0px' : `var(--mdc-drawer-width) ${widthDesktopDivided[2] === '%' ? '' : '/ 2'}`}); } } - #root.editor { + .pop-up.editor { position: inherit !important; width: 100% !important; padding: 18px !important; @@ -507,44 +623,13 @@ export function handlePopUp(context) { addStyles(hass, context, headerStyles, customStyles, state, entityId, stateChanged); } - // Initialize pop-up - - const initPopUp = setTimeout(() => { - const initEvent = new Event('popUpInitialized'); - - if (!context.element) { - context.element = context.getRootNode().querySelector('#root'); - } - - if (context.element && ( - !context.popUp - || stateChanged - || (editor && !context.editorModeAdded) - )){ - context.popUp = context.element; - - if (editor && context.popUp && !context.editorModeAdded) { - context.popUp.classList.add('editor'); - context.popUp.classList.remove('close-pop-up', 'open-pop-up'); - createPopUp(); - context.editorModeAdded = true; - } else { - createPopUp(); - } - - clearTimeout(initPopUp); - window.dispatchEvent(initEvent); - - } else if (!editor && context.popUp && context.editorModeAdded) { - context.popUp.classList.remove('editor'); - createPopUp(); - context.editorModeAdded = false; - } - }, 0); - // Pop-up triggers - function popUpTriggers(triggerEntityState) { + let triggerEntity = config.trigger_entity ? config.trigger_entity : ''; + let triggerState = config.trigger_state ? config.trigger_state : ''; + let triggerClose = config.trigger_close ? config.trigger_close : false; + + function popUpTriggers(oldTriggerEntityState, triggerEntityState) { if (!triggerEntityState || triggerEntityState === oldTriggerEntityState) { return; } @@ -592,6 +677,6 @@ export function handlePopUp(context) { if (context.popUp && triggerEntity) { const triggerEntityState = hass.states[triggerEntity].state; - popUpTriggers(triggerEntityState); + popUpTriggers(oldTriggerEntityState, triggerEntityState); } } \ No newline at end of file diff --git a/src/cards/separator.ts b/src/cards/separator.ts index 276c52e..f4fdcae 100644 --- a/src/cards/separator.ts +++ b/src/cards/separator.ts @@ -1,25 +1,4 @@ -import { - addStyles, - createIcon, - updateIcon, - isColorCloseToWhite, - convertToRGBA, - getIconColor, - getIconStyles -} from '../tools/style.ts'; -import { - initializeContent, - checkEditor, - checkResources -} from '../tools/init.ts'; -import { - fireEvent, - forwardHaptic, - navigate, - toggleEntity, - hasStateChanged -} from '../tools/utils.ts'; -import { addActions } from '../tools/tap-actions.ts'; +import { addStyles } from '../tools/style.ts'; import { getVariables } from '../var/cards.ts'; export function handleSeparator(context) { @@ -30,31 +9,8 @@ export function handleSeparator(context) { let { customStyles, - entityId, icon, - name, - widthDesktop, - widthDesktopDivided, - isSidebarHidden, - state, - stateChanged, - stateOn, - formatedState, - riseAnimation, - marginCenter, - popUpOpen, - rgbaColor, - rgbColor, - bgOpacity, - shadowOpacity, - bgBlur, - iconColorOpacity, - iconColor, - iconFilter, - iconStyles, - haStyle, - themeBgColor, - color, + name } = getVariables(context, config, hass, editor); if (!context.separatorAdded || editor) { diff --git a/src/editor/bubble-card-editor.ts b/src/editor/bubble-card-editor.ts index c96716e..cd83659 100644 --- a/src/editor/bubble-card-editor.ts +++ b/src/editor/bubble-card-editor.ts @@ -112,6 +112,10 @@ export default class BubbleCardEditor extends LitElement { return this._config.close_on_click || false; } + get _background_camera() { + return this._config.background_camera || false; + } + get _icon_open() { return this._config.icon_open || ''; } @@ -136,7 +140,7 @@ export default class BubbleCardEditor extends LitElement { return this._config.auto_order || false; } - get _highlightCurrentview() { + get _highlight_current_view() { return this._config.highlight_current_view || false; } @@ -144,6 +148,14 @@ export default class BubbleCardEditor extends LitElement { return this._config.show_state || false; } + get _hide_backdrop() { + return this._config.hide_backdrop || false; + } + + get _hide_gradient() { + return this._config.hide_gradient || false; + } + render() { if (!this.hass) { return html``; @@ -223,18 +235,9 @@ export default class BubbleCardEditor extends LitElement { return html`
${this.makeDropdown("Card type", "card_type", cardTypeList)} -

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?
+

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.
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.
+ Since v1.7.0, the optimized mode has been removed to ensure stability and to simplify updates for everyone. However, if your pop-up content still appears on the screen during page loading, you can install this similar fix. Optional - Close the pop-up after any click or tap
+ + +
+ +
+

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)} @@ -361,7 +375,7 @@ export default class BubbleCardEditor extends LitElement { style="width: 100%;" > - Set ‘Background blur’ to 0 if your pop-up animations are rendering at low FPS. + + +
+ +
+
+ Set this toggle to true on the first pop-up of your main dashboard to disable the backdrop on all pop-ups. ${this.makeVersion()}
`; @@ -519,8 +544,8 @@ export default class BubbleCardEditor extends LitElement { @@ -528,6 +553,17 @@ export default class BubbleCardEditor extends LitElement { + + +
+ +
+
${this.makeVersion()} `; @@ -601,7 +637,7 @@ export default class BubbleCardEditor extends LitElement { You need to add a card type first.

The Bubble Card ${version} changelog is available here. -

Column fix: If you experience some issues with your dashboard layout, such as empty columns or misaligned cards. You can apply a fix that restores the behavior of the previous versions by adding column_fix: true in YAML to the first Bubble Card on your dashboard. Then refresh the page. +

Column fix: If you experience some issues with your dashboard layout, such as empty columns or misaligned cards. You can apply a fix that restores the behavior of the previous versions by adding column_fix: true in YAML to the first Bubble Card on your dashboard. You can also try to add a negative value to find the one that fit your dashboard like column_fix: -10. Then refresh the page.


Almost everything is available in the GUI editor, but in the YAML editor you can add your own custom styles, create custom buttons or modify the tap actions of all cards. You can find more details on my GitHub page.

diff --git a/src/tools/init.ts b/src/tools/init.ts index 82859d8..bb99ddb 100644 --- a/src/tools/init.ts +++ b/src/tools/init.ts @@ -4,17 +4,18 @@ import { version } from '../var/version.ts'; export function initializeContent(context) { if (!context.content) { - context.attachShadow({ - mode: 'open' - }); - context.shadowRoot.innerHTML = ` - -
-
-
- `; - context.card = context.shadowRoot.querySelector("ha-card"); - context.content = context.shadowRoot.querySelector("div"); + let shadow = context.shadowRoot || context.attachShadow({ mode: 'open' }); + let fragment = document.createDocumentFragment(); + let card = document.createElement("ha-card"); + card.style.cssText = "background: none; border: none; box-shadow: none; border-radius: 16px;"; + let content = document.createElement("div"); + content.className = "card-content"; + content.style.padding = "0"; + card.appendChild(content); + fragment.appendChild(card); + shadow.appendChild(fragment); + context.card = card; + context.content = content; } } diff --git a/src/tools/style.ts b/src/tools/style.ts index ac7b49b..a69f51d 100644 --- a/src/tools/style.ts +++ b/src/tools/style.ts @@ -7,7 +7,7 @@ export const addStyles = function(hass, context, styles, customStyles, state, en const executeStyles = () => { // Evaluate customStyles if it exists, else assign an empty string - const customStylesEval = customStyles ? eval('`' + customStyles + '`') : ''; + const customStylesEval = customStyles ? Function('hass', 'entityId', 'state', 'return `' + customStyles + '`;')(hass, entityId, state) : ''; let styleAddedKey = styles + 'Added'; // Append 'Added' to the styles value // Check if the style has changed @@ -64,6 +64,11 @@ export function createIcon(context, entityId, icon, iconContainer, editor) { setInterval(() => { hass = context._hass; + + if (!entityId.startsWith('media_player.')) { + return; + } + if (entityId && hass.states[entityId]) { context.currentEntityPicture = hass.states[entityId].attributes.entity_picture; if (context.currentEntityPicture !== context.previousEntityPicture) { @@ -123,8 +128,9 @@ export function isColorCloseToWhite(rgbColor) { return true; } +let rgbaColor; + export function convertToRGBA(color, opacity, lighten = 1) { - let rgbaColor = ''; if (color.startsWith('#')) { if (color.length === 4) { // Short hexadecimal color let r = Math.min(255, parseInt(color.charAt(1).repeat(2), 16) * lighten), @@ -139,11 +145,7 @@ export function convertToRGBA(color, opacity, lighten = 1) { } } else if (color.startsWith('rgb')) { let rgbValues = color.match(/\d+/g); - if (color.includes('rgba')) { // Color is already in RGBA - rgbaColor = "rgba(" + Math.min(255, rgbValues[0] * lighten) + ", " + Math.min(255, rgbValues[1] * lighten) + ", " + Math.min(255, rgbValues[2] * lighten) + ", " + opacity + ")"; - } else { // Color is in RGB - rgbaColor = "rgba(" + Math.min(255, rgbValues[0] * lighten) + ", " + Math.min(255, rgbValues[1] * lighten) + ", " + Math.min(255, rgbValues[2] * lighten) + ", " + opacity + ")"; - } + rgbaColor = "rgba(" + Math.min(255, rgbValues[0] * lighten) + ", " + Math.min(255, rgbValues[1] * lighten) + ", " + Math.min(255, rgbValues[2] * lighten) + ", " + opacity + ")"; } return rgbaColor; } diff --git a/src/tools/tap-actions.ts b/src/tools/tap-actions.ts index c2350e3..7da5363 100644 --- a/src/tools/tap-actions.ts +++ b/src/tools/tap-actions.ts @@ -72,8 +72,6 @@ export function sendActionEvent(element, config, action) { } }; - console.log("Action =", action, "Action config =", actionConfig); - setTimeout(() => { const event = new Event('hass-action', { bubbles: true, diff --git a/src/tools/url-listener.ts b/src/tools/url-listener.ts index e8c51a5..03f63f9 100644 --- a/src/tools/url-listener.ts +++ b/src/tools/url-listener.ts @@ -3,6 +3,7 @@ const event = new Event('urlChanged'); export function addUrlListener() { if (!window.eventAdded) { + console.log("Event added") // 'urlChanged' custom event window.eventAdded = true; @@ -14,16 +15,17 @@ export function addUrlListener() { function urlChanged() { let count = 0; + window.dispatchEvent(event); - // Send more events for when the connexion was lost - const intervalId = setInterval(() => { - if (count < 10) { - window.dispatchEvent(event); - count++; - } else { - clearInterval(intervalId); - } - }, 100); + // // Send more events for when the connexion was lost + // const intervalId = setInterval(() => { + // if (count < 10) { + // window.dispatchEvent(event); + // count++; + // } else { + // clearInterval(intervalId); + // } + // }, 100); } // Check url when pop-ups are initialized diff --git a/src/var/cards.ts b/src/var/cards.ts index 9429dab..9b85d5b 100644 --- a/src/var/cards.ts +++ b/src/var/cards.ts @@ -12,12 +12,18 @@ let popUpOpen; let rgbaColor; let rgbColor; let formatedState; +let haStyle; +let themeBgColor; export function getVariables(context, config, hass, editor) { let customStyles = !config.styles ? '' : config.styles; let entityId = config.entity && hass.states[config.entity] ? config.entity : ''; + let state = entityId ? hass.states[entityId].state : ''; + hasStateChanged(context, hass, entityId); + let stateChanged = context.stateChanged; + let icon = !config.icon && entityId ? hass.states[entityId].attributes.icon || hass.states[entityId].attributes.entity_picture || '' : config.icon || ''; @@ -29,9 +35,6 @@ export function getVariables(context, config, hass, editor) { let widthDesktop = config.width_desktop || '540px'; let widthDesktopDivided = widthDesktop ? widthDesktop.match(/(\d+)(\D+)/) : ''; let isSidebarHidden = config.is_sidebar_hidden || false; - let state = entityId ? hass.states[entityId].state : ''; - hasStateChanged(context, hass, entityId); - let stateChanged = context.stateChanged; let stateOn = ['on', 'open', 'cleaning', 'true', 'home', 'playing'].includes(state) || (Number(state) !== 0 && !isNaN(Number(state))); let riseAnimation = config.rise_animation !== undefined ? config.rise_animation : true; let marginCenter = config.margin @@ -46,14 +49,13 @@ export function getVariables(context, config, hass, editor) { iconFilter } = getIconColor(hass, entityId, stateOn, isColorCloseToWhite); let iconStyles = getIconStyles(entityId, stateOn, iconColor, iconFilter); - let haStyle = getComputedStyle(document.body); - let themeBgColor = haStyle.getPropertyValue('--ha-card-background') || haStyle.getPropertyValue('--card-background-color'); + haStyle = !haStyle ? getComputedStyle(document.body) : ''; + themeBgColor = !themeBgColor ? haStyle.getPropertyValue('--ha-card-background') || haStyle.getPropertyValue('--card-background-color') : ''; let color = config.bg_color ? config.bg_color : themeBgColor; - if (color && (!context.color || color !== context.color)) { + if (color && (!rgbaColor || rgbaColor !== context.color)) { const lighten = 1.02; rgbaColor = convertToRGBA(color, (bgOpacity / 100), lighten); - document.body.style.setProperty('--bubble-pop-up-background', rgbaColor); - context.color = color; + context.color = rgbaColor; window.color = color; } diff --git a/src/var/version.ts b/src/var/version.ts index a7e6e7b..923ea5d 100644 --- a/src/var/version.ts +++ b/src/var/version.ts @@ -1 +1 @@ -export let version = 'v1.6.4'; \ No newline at end of file +export let version = 'v1.7.0'; \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index e6c956e..9dc1aaa 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,22 +6,25 @@ module.exports = [ mode: 'production', entry: { 'bubble-card': './src/bubble-card.ts', - 'bubble-pop-up': './src/bubble-pop-up.ts', + 'bubble-pop-up-fix': './src/bubble-pop-up-fix.ts' }, output: { path: path.resolve(__dirname, 'dist'), - filename: '[name].js', - }, + filename: '[name].js' + } }, + + // My Home Assistant test server + { mode: 'production', entry: { 'bubble-card': './src/bubble-card.ts', - 'bubble-pop-up': './src/bubble-pop-up.ts', + 'bubble-pop-up-fix': './src/bubble-pop-up-fix.ts' }, output: { path: path.resolve('/Volumes/config/www'), - filename: '[name].js', - }, + filename: '[name].js' + } } -]; \ No newline at end of file +];