diff --git a/src/bubble-card.js b/src/bubble-card.js
index e9cb480e..08616438 100644
--- a/src/bubble-card.js
+++ b/src/bubble-card.js
@@ -1,318 +1,214 @@
+const version = 'v0.0.1-beta.6';
+
class BubbleCard extends HTMLElement {
- set hass(hass) {
- // Initialize the content if it's not there yet.
- if (!this.content) {
- this.attachShadow({ mode: 'open' });
- this.shadowRoot.innerHTML = `
-
${button}
- `; - return buttonElement; - }; - - const updateButtonStyle = (buttonElement, lightEntity) => { - if (hass.states[lightEntity].attributes.rgb_color) { - const rgbColor = hass.states[lightEntity].attributes.rgb_color; - const rgbColorOpacity = `rgba(${rgbColor[0]}, ${rgbColor[1]}, ${rgbColor[2]}, 0.5)`; - buttonElement.style.backgroundColor = rgbColorOpacity; - buttonElement.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)'; - } else { - buttonElement.style.backgroundColor = 'rgba(0,0,0,0)'; - buttonElement.style.border = '1px solid var(--primary-text-color)'; - } - }; - - if (!this.buttonsAdded) { - const buttonsContainer = document.createElement("div"); - buttonsContainer.setAttribute("class", "horizontal-buttons-stack-container"); - this.content.appendChild(buttonsContainer); - } - - const updateButtonsOrder = () => { - let buttonsList = []; - let i = 1; - while (this.config[i + '_name']) { - const prefix = i + '_'; - const button = this.config[prefix + 'name']; - const pirSensor = this.config[prefix + 'pir_sensor']; - const icon = this.config[prefix + 'icon']; - const link = this.config[prefix + 'link']; - const lightEntity = this.config[prefix + 'entity']; - buttonsList.push({button, pirSensor, icon, link, lightEntity}); - i++; - } - - // if (this.config.auto_order) { - // buttonsList.sort((a, b) => { - // let aTime = hass.states[a.pirSensor].last_updated; - // let bTime = hass.states[b.pirSensor].last_updated; - // return aTime < bTime ? 1 : -1; - // }); - // } - - if (this.config.auto_order) { - buttonsList.sort((a, b) => { - // Vérifier si l'état du capteur PIR est "on" pour les deux boutons - if (hass.states[a.pirSensor].state === "on" && hass.states[b.pirSensor].state === "on") { - let aTime = hass.states[a.pirSensor].last_updated; - let bTime = hass.states[b.pirSensor].last_updated; - return aTime < bTime ? 1 : -1; - } - // Si seulement a.pirSensor est "on", placer a avant b - else if (hass.states[a.pirSensor].state === "on") { - return -1; - } - // Si seulement b.pirSensor est "on", placer b avant a - else if (hass.states[b.pirSensor].state === "on") { - return 1; + + if (!this.styleAdded && this.editor !== true) { + const styleElement = document.createElement('style'); + const headerStyleElement = document.createElement('style'); + + const styles = ` + ha-card { + margin-top: 0 !important; + background: none !important; + border: none !important; + } + .card-content { + width: 100% !important; + padding: 0 !important; + } + #root { + position: fixed !important; + margin: 0 -7px; + width: calc(100% - 38px); + background-color: var(--ha-card-background,var(--card-background-color)); + border-radius: 42px; + top: calc(100% + ${marginTopMobile} + 54px); /*136px*/ + display: grid !important; + grid-gap: 12px !important; + gap: 12px !important; + grid-auto-rows: min-content; + padding: 18px 18px 220px 18px !important; + height: calc(100% - 240px) !important; + -ms-overflow-style: none; /* for Internet Explorer, Edge */ + scrollbar-width: none; /* for Firefox */ + overflow-y: auto; + overflow-x: hidden; + z-index: 1 !important; /* Higher value hide the more-info panel */ + } + #root > bubble-card:first-child::after { + content: ''; + display: block; + position: sticky; + top: 0; + left: -50px; + margin: -70px 0 -35px 0; + width: 200%; + height: 100px; + background: linear-gradient(0deg, rgba(79, 69, 87, 0) 0%, var(--ha-card-background,var(--card-background-color)) 80%); + z-index: 0; + } + #root::-webkit-scrollbar { + display: none; /* for Chrome, Safari, and Opera */ + } + #root > bubble-card:first-child { + position: sticky; + top: 0; + z-index: 5; + background: none !important; + } + #root.open-pop-up { + transform: translateY(-100%); + transition: transform .4s !important; + } + #root.open-pop-up > * { + /* Block child items to overflow and if they do clip them */ + max-width: calc(100vw - 38px); + overflow-x: clip; + } + #root.close-pop-up { + transform: translateY(0%); + transition: transform .4s !important; + /* animation: hide 1s forwards; */ + } + @media only screen and (min-width: 768px) { + #root { + top: calc(100% + ${marginTopDesktop} + 54px); + width: calc(${widthDesktop} - 36px) !important; + left: calc(50% - ${widthDesktopDivided[1] / 2}${widthDesktopDivided[2]}); + margin: 0 !important; + } + } + @media only screen and (min-width: 870px) { + #root { + top: calc(100% + ${marginTopDesktop} + 54px); + width: calc(${widthDesktop} - ${isSidebarHidden === true ? '0px' : '92px'}) !important; + left: calc(50% - ${widthDesktopDivided[1] / 2}${widthDesktopDivided[2]} + ${isSidebarHidden === true ? '0px' : '56px'}); + margin: 0 !important; + } + } + `; + + styleElement.innerHTML = styles; + headerStyleElement.innerHTML = headerStyles; + popUp.appendChild(styleElement); + this.content.appendChild(headerStyleElement); + this.styleAdded = true; + } else if (this.editor === true) { + const styleElement = this.getRootNode().querySelector('#root').querySelector("style"); + if (styleElement) { + popUp.removeChild(styleElement); } - // Si aucun des capteurs PIR n'est "on", arrangement basé seulement sur l'état de last updated même si off - let aTime = hass.states[a.pirSensor].last_updated; - let bTime = hass.states[b.pirSensor].last_updated; - return aTime < bTime ? 1 : -1; - }); - } - - if (!this.buttonsAdded) { - const buttonsContainer = this.content.querySelector(".horizontal-buttons-stack-container"); - const buttons = {}; - buttonsList.forEach(button => { - const buttonElement = createButton(button.button, button.link, button.icon); - // Stocker l'élément de bouton en utilisant son lien comme clé - buttons[button.link] = buttonElement; - buttonsContainer.appendChild(buttonElement); - }); - this.buttonsAdded = true; - this.buttons = buttons; - } - - let currentPosition = 0; - const margin = 12; - buttonsList.forEach((button, index) => { - let buttonElement = this.buttons[button.link]; - if (buttonElement) { - let buttonWidth = localStorage.getItem(`buttonWidth-${button.link}`); - let buttonContent = localStorage.getItem(`buttonContent-${button.link}`); - if (!buttonWidth || buttonWidth === '0' || buttonContent !== buttonElement.innerHTML || this.editor === 'true') { - buttonWidth = buttonElement.offsetWidth; - localStorage.setItem(`buttonWidth-${button.link}`, buttonWidth); - localStorage.setItem(`buttonContent-${button.link}`, buttonElement.innerHTML); - } - buttonElement.style.transform = `translateX(${currentPosition}px)`; - currentPosition += parseInt(buttonWidth) + margin; - } - if (button.lightEntity) { - updateButtonStyle(buttonElement, button.lightEntity); + popUp.style.backgroundColor = 'var(--ha-card-background,var(--card-background-color))'; + popUp.style.padding = '16px'; + popUp.style.borderRadius = '42px'; + popUp.style.gridGap = '12px'; + popUp.style.gap = '12px'; + const headerStyleElement = document.createElement('style'); + headerStyleElement.innerHTML = headerStyles; + this.content.appendChild(headerStyleElement); + this.styleAdded = false; } - }); } - updateButtonsOrder(); - - if (!this.styleAdded) { - const styleElement = document.createElement('style'); - const styles = ` - .horizontal-buttons-stack { - width: 100%; - margin-top: 0 !important; - background: none !important; - position: fixed; - height: 51px; - bottom: 16px; - /* transform: translateY(200px); */ - /* animation: from-bottom 1.3s forwards; */ - 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);} - 62% {transform: translateY(-2px);} - 70% {transform: translateY(0);} - 100% {transform: translateY(0);} - } - .horizontal-buttons-stack-container { - width: max-content; - position: relative; - height: 51px; - } - .button { - display: flex; - position: absolute; - box-sizing: border-box; - border: 1px solid var(--primary-text-color); - align-items: center; - height: 50px; - white-space: nowrap; - width: auto; - border-radius: 25px; - z-index: 2; - padding: 16px; - transition: background-color 1s, border 1s, transform 1s; - color: var(--primary-text-color); - } - .icon { - height: 24px; - margin-right: 8px; - } - .card-content { - width: 100%; - box-sizing: border-box; - margin: 0 -36px !important; - padding: 0 36px !important; - overflow: scroll !important; - -ms-overflow-style: none; - scrollbar-width: none; - 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%); + // Initialize horizontal buttons stack + if (this.config.card_type === 'horizontal-buttons-stack') { + const createButton = (button, link, icon) => { + const buttonElement = document.createElement("button"); + buttonElement.setAttribute("onclick", `window.location.href='${link}';`); + buttonElement.setAttribute("class", "button"); + buttonElement.innerHTML = ` +${button}
+ `; + return buttonElement; + }; + + const updateButtonStyle = (buttonElement, lightEntity) => { + if (hass.states[lightEntity].attributes.rgb_color) { + const rgbColor = hass.states[lightEntity].attributes.rgb_color; + const rgbColorOpacity = `rgba(${rgbColor[0]}, ${rgbColor[1]}, ${rgbColor[2]}, 0.5)`; + buttonElement.style.backgroundColor = rgbColorOpacity; + buttonElement.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)'; + } else { + buttonElement.style.backgroundColor = 'rgba(0,0,0,0)'; + buttonElement.style.border = '1px solid var(--primary-text-color)'; } - .horizontal-buttons-stack::before { - content: ''; - position: fixed; - left: 60px; /* 0px if no sidebar */ - display: block; - /*background: linear-gradient(0deg, var(--background-color, var(--primary-background-color)) 50%, rgba(79, 69, 87, 0));*/ - background: radial-gradient(ellipse at bottom center, var(--background-color, var(--primary-background-color)) 30%, 50%, rgba(79, 69, 87, 0) 68%); - width: 100%; - height: 70px; - pointer-events: none; + }; + + if (!this.buttonsAdded) { + const buttonsContainer = document.createElement("div"); + buttonsContainer.setAttribute("class", "horizontal-buttons-stack-container"); + this.content.appendChild(buttonsContainer); + } + + const updateButtonsOrder = () => { + let buttonsList = []; + let i = 1; + while (this.config[i + '_name']) { + const prefix = i + '_'; + const button = this.config[prefix + 'name']; + const pirSensor = this.config[prefix + 'pir_sensor']; + const icon = this.config[prefix + 'icon']; + const link = this.config[prefix + 'link']; + const lightEntity = this.config[prefix + 'entity']; + buttonsList.push({ + button, + pirSensor, + icon, + link, + lightEntity + }); + i++; } - .card-content::-webkit-scrollbar { - display: none; + + if (this.config.auto_order) { + buttonsList.sort((a, b) => { + // Check if both PIR sensors are defined + if (a.pirSensor && b.pirSensor) { + // Check if the PIR sensor state is "on" for both buttons + if (hass.states[a.pirSensor].state === "on" && hass.states[b.pirSensor].state === "on") { + let aTime = hass.states[a.pirSensor].last_updated; + let bTime = hass.states[b.pirSensor].last_updated; + return aTime < bTime ? 1 : -1; + } + // If only a.pirSensor is "on", place a before b + else if (hass.states[a.pirSensor].state === "on") { + return -1; + } + // If only b.pirSensor is "on", place b before a + else if (hass.states[b.pirSensor].state === "on") { + return 1; + } + // If neither PIR sensor is "on", arrangement based only on the state of last updated even if off + let aTime = hass.states[a.pirSensor].last_updated; + let bTime = hass.states[b.pirSensor].last_updated; + return aTime < bTime ? 1 : -1; + } + // If a.pirSensor is not defined, place a after b + else if (!a.pirSensor) { + return 1; + } + // If b.pirSensor is not defined, place b after a + else if (!b.pirSensor) { + return -1; + } + }); } - @media only screen and (min-width: 768px) { - .card-content { - position: fixed; - width: 538px !important; - left: calc(50% - 246px); - padding: 0 26px !important; - } + + if (!this.buttonsAdded) { + const buttonsContainer = this.content.querySelector(".horizontal-buttons-stack-container"); + const buttons = {}; + buttonsList.forEach(button => { + const buttonElement = createButton(button.button, button.link, button.icon); + // Store the button element using its link as key + buttons[button.link] = buttonElement; + buttonsContainer.appendChild(buttonElement); + }); + this.buttonsAdded = true; + this.buttons = buttons; } - @media only screen and (min-width: 870px) { - .card-content { + + let currentPosition = 0; + const margin = 12; + buttonsList.forEach((button, index) => { + let buttonElement = this.buttons[button.link]; + if (buttonElement) { + let buttonWidth = localStorage.getItem(`buttonWidth-${button.link}`); + let buttonContent = localStorage.getItem(`buttonContent-${button.link}`); + if (!buttonWidth || buttonWidth === '0' || buttonContent !== buttonElement.innerHTML || this.editor === true) { + buttonWidth = buttonElement.offsetWidth; + localStorage.setItem(`buttonWidth-${button.link}`, buttonWidth); + localStorage.setItem(`buttonContent-${button.link}`, buttonElement.innerHTML); + } + buttonElement.style.transform = `translateX(${currentPosition}px)`; + currentPosition += parseInt(buttonWidth) + margin; + } + if (button.lightEntity) { + updateButtonStyle(buttonElement, button.lightEntity); + } + }); + } + + updateButtonsOrder(); + + if (!this.styleAdded) { + const styleElement = document.createElement('style'); + const styles = ` + .horizontal-buttons-stack { + width: 100%; + margin-top: 0 !important; + background: none !important; position: fixed; - width: 538px !important; - left: calc(50% - 218px); - padding: 0 26px !important; - } - } - `; - - styleElement.innerHTML = styles; - this.card.classList.add('horizontal-buttons-stack'); - this.content.querySelector(".horizontal-buttons-stack-container").appendChild(styleElement); - this.styleAdded = true; - } - - if (this.editor === 'true') { - if (!this.editorStyleAdded) { - const styleElement = document.createElement('style'); - const styles = ` - .horizontal-buttons-stack { - width: 100%; - margin-top: 0 !important; - background: none !important; - position: relative; - height: auto; - bottom: 0px; - overflow: hidden; - } - .horizontal-buttons-stack::before { - content: ''; - position: absolute; - top: 0; - left: 0; - display: block; - background: none; - width: 100%; - height: 0; - } - .card-content { - width: 100% !important; - position: relative; + height: 51px; + bottom: 16px; + /* transform: translateY(200px); */ + /* animation: from-bottom 1.3s forwards; */ + 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);} + 62% {transform: translateY(-2px);} + 70% {transform: translateY(0);} + 100% {transform: translateY(0);} + } + .horizontal-buttons-stack-container { + width: max-content; + position: relative; + height: 51px; + } + .button { + display: flex; + position: absolute; + box-sizing: border-box; + border: 1px solid var(--primary-text-color); + align-items: center; + height: 50px; + white-space: nowrap; + width: auto; + border-radius: 25px; + z-index: 2; + padding: 16px; + transition: background-color 1s, border 1s, transform 1s; + color: var(--primary-text-color); + } + .icon { + height: 24px; + margin-right: 8px; + } + .card-content { + width: 100%; + box-sizing: border-box; + margin: 0 -36px !important; + padding: 0 36px !important; + overflow: scroll !important; + -ms-overflow-style: none; + scrollbar-width: none; + 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; + background: linear-gradient(0deg, var(--background-color, var(--primary-background-color)) 50%, rgba(79, 69, 87, 0)); + width: 200%; + height: 100px; + } + .card-content::-webkit-scrollbar { + display: none; + } + @media only screen and (min-width: 768px) { + .card-content { + position: fixed; + width: 538px !important; + left: calc(50% - 246px); + padding: 0 26px !important; + } + } + @media only screen and (min-width: 870px) { + .card-content { + position: fixed; + width: 538px !important; + left: calc(50% - 218px); + padding: 0 26px !important; + } + } + `; + + styleElement.innerHTML = styles; + this.card.classList.add('horizontal-buttons-stack'); + this.content.querySelector(".horizontal-buttons-stack-container").appendChild(styleElement); + this.styleAdded = true; + } + + if (this.editor === true) { + if (!this.editorStyleAdded) { + const styleElement = document.createElement('style'); + const styles = ` + .horizontal-buttons-stack { + position: relative; + height: 51px; + bottom: 0px; + overflow: hidden; + } + .horizontal-buttons-stack::before { + top: -32px; + left: -100%; + background: none; + width: 100%; + height: 0; + } + .card-content { + position: relative; + 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%); + } + @media only screen and (min-width: 870px) { + .card-content { + left: calc(50% - 230px); + } + } + `; + styleElement.innerHTML = styles; + this.card.appendChild(styleElement); + this.editorStyleAdded = true; } - `; - styleElement.innerHTML = styles; - this.card.appendChild(styleElement); - this.editorStyleAdded = true; - } - } else if (this.card.querySelector("ha-card > style")) { - const styleElement = this.card.querySelector("ha-card > style"); - this.card.removeChild(styleElement); - } - } - - // Initialize button - if (this.config.card_type === 'button') { - if (!this.buttonAdded) { - const buttonContainer = document.createElement("div"); - buttonContainer.setAttribute("class", "button-container"); - this.content.appendChild(buttonContainer); - } - - const entityId = this.config.entity; - const icon = !this.config.icon ? hass.states[entityId].attributes.icon || '' : this.config.icon; - const name = !this.config.name ? hass.states[entityId].attributes.friendly_name || '' : this.config.name; - const buttonType = this.config.button_type || 'switch'; - const state = !entityId ? '' : hass.states[entityId].state; - let currentBrightness = !entityId ? '' : hass.states[entityId].attributes.brightness || 0; - let currentVolume = !entityId ? '' : hass.states[entityId].attributes.volume_level || 0; - - const iconContainer = document.createElement('div'); - iconContainer.setAttribute('class', 'icon-container'); - - const nameContainer = document.createElement('div'); - nameContainer.setAttribute('class', 'nameContainer'); - - const switchButton = document.createElement('div'); - switchButton.setAttribute('class', 'switch-button'); - - const rangeSlider = document.createElement('div'); - rangeSlider.setAttribute('class', 'range-slider'); - - const rangeFill = document.createElement('div'); - - rangeFill.setAttribute('class', 'range-fill'); - if (entityId && entityId.startsWith("light.") && buttonType === 'slider') { - rangeFill.style.transform = `translateX(${(currentBrightness / 255) * 100}%)`; - } - else if (entityId && entityId.startsWith("media_player.") && buttonType === 'slider') { - rangeFill.style.transform = `translateX(${currentVolume * 100}%)`; - } - - if (!this.buttonContainer) { - this.buttonContainer = this.content.querySelector(".button-container"); - - if (buttonType === 'slider' && !this.buttonAdded) { - this.buttonContainer.appendChild(rangeSlider); - rangeSlider.appendChild(iconContainer); - rangeSlider.appendChild(nameContainer); - rangeSlider.appendChild(rangeFill); - this.rangeFill = this.content.querySelector(".range-fill"); - } - else if (buttonType === 'switch') { - this.buttonContainer.appendChild(switchButton); - switchButton.appendChild(iconContainer); - switchButton.appendChild(nameContainer); - this.switchButton = this.content.querySelector(".switch-button"); + } else if (this.card.querySelector("ha-card > style")) { + const styleElement = this.card.querySelector("ha-card > style"); + this.card.removeChild(styleElement); } - - iconContainer.innerHTML = `${name}
`; - - this.buttonAdded = true; } - - let isDragging = false; - let brightness = currentBrightness; - let volume = currentVolume; - - function tapFeedback(content) { - let feedbackElement = content.querySelector('.feedback-element'); - if (!feedbackElement) { - feedbackElement = document.createElement('div'); - feedbackElement.setAttribute('class', 'feedback-element'); - content.appendChild(feedbackElement); + + // Initialize button + if (this.config.card_type === 'button') { + if (!this.buttonAdded) { + const buttonContainer = document.createElement("div"); + buttonContainer.setAttribute("class", "button-container"); + this.content.appendChild(buttonContainer); } - - feedbackElement.style.animation = 'tap-feedback .5s'; - setTimeout(() => { - feedbackElement.style.animation = 'none'; - content.removeChild(feedbackElement); - }, 500); - } - - function handleStart(e) { - if (e.target === iconContainer.querySelector('ha-icon')) { - iconContainer.addEventListener('click', event => { - fireEvent(this, "hass-more-info", { entityId: entityId }); - }); - } else { - isDragging = true; - updateRange(e.pageX || e.touches[0].pageX); - document.addEventListener('mouseup', handleEnd); - document.addEventListener('touchend', handleEnd); - document.addEventListener('mousemove', handleMove); - document.addEventListener('touchmove', handleMove); - } - } - - function handleEnd() { - isDragging = false; - - document.removeEventListener('mouseup', handleEnd); - document.removeEventListener('touchend', handleEnd); - document.removeEventListener('mousemove', handleMove); - document.removeEventListener('touchmove', handleMove); - - if (currentBrightness !== brightness && entityId.startsWith("light.")) { - currentBrightness = brightness; - hass.callService('light', 'turn_on', { - entity_id: entityId, - brightness: currentBrightness - }); + + const entityId = this.config.entity; + const icon = !this.config.icon ? hass.states[entityId].attributes.icon || '' : this.config.icon; + const name = !this.config.name ? hass.states[entityId].attributes.friendly_name || '' : this.config.name; + const buttonType = this.config.button_type || 'switch'; + const state = !entityId ? '' : hass.states[entityId].state; + let currentBrightness = !entityId ? '' : hass.states[entityId].attributes.brightness || 0; + let currentVolume = !entityId ? '' : hass.states[entityId].attributes.volume_level || 0; + + const iconContainer = document.createElement('div'); + iconContainer.setAttribute('class', 'icon-container'); + + const nameContainer = document.createElement('div'); + nameContainer.setAttribute('class', 'nameContainer'); + + const switchButton = document.createElement('div'); + switchButton.setAttribute('class', 'switch-button'); + + const rangeSlider = document.createElement('div'); + rangeSlider.setAttribute('class', 'range-slider'); + + const rangeFill = document.createElement('div'); + + rangeFill.setAttribute('class', 'range-fill'); + if (entityId && entityId.startsWith("light.") && buttonType === 'slider') { + rangeFill.style.transform = `translateX(${(currentBrightness / 255) * 100}%)`; + } else if (entityId && entityId.startsWith("media_player.") && buttonType === 'slider') { + rangeFill.style.transform = `translateX(${currentVolume * 100}%)`; } - else if (currentVolume !== volume && entityId.startsWith("media_player.")) { - currentVolume = volume; - hass.callService('media_player', 'volume_set', { - entity_id: entityId, - volume_level: currentVolume - }); + + if (!this.buttonContainer) { + this.buttonContainer = this.content.querySelector(".button-container"); + + if (buttonType === 'slider' && !this.buttonAdded) { + this.buttonContainer.appendChild(rangeSlider); + rangeSlider.appendChild(iconContainer); + rangeSlider.appendChild(nameContainer); + rangeSlider.appendChild(rangeFill); + this.rangeFill = this.content.querySelector(".range-fill"); + } else if (buttonType === 'switch') { + this.buttonContainer.appendChild(switchButton); + switchButton.appendChild(iconContainer); + switchButton.appendChild(nameContainer); + this.switchButton = this.content.querySelector(".switch-button"); + } + + iconContainer.innerHTML = `${name}
`; + + this.buttonAdded = true; } - } - - function handleMove(e) { - if (isDragging) { - updateRange(e.pageX || e.touches[0].pageX); + + let isDragging = false; + let brightness = currentBrightness; + let volume = currentVolume; + + function tapFeedback(content) { + let feedbackElement = content.querySelector('.feedback-element'); + if (!feedbackElement) { + feedbackElement = document.createElement('div'); + feedbackElement.setAttribute('class', 'feedback-element'); + content.appendChild(feedbackElement); + } + + feedbackElement.style.animation = 'tap-feedback .5s'; + setTimeout(() => { + feedbackElement.style.animation = 'none'; + content.removeChild(feedbackElement); + }, 500); } - } - - if (!this.eventAdded && buttonType === 'switch') { - switchButton.addEventListener('click', () => tapFeedback(this.switchButton)); - //switchButton.addEventListener('touchstart', () => tapFeedback(this.switchButton)); - switchButton.addEventListener('click', function(e) { - if (e.target.closest('ha-icon')) { //e.target === this.querySelector('ha-icon') - fireEvent(this, "hass-more-info", { entityId: entityId }); + + function handleStart(e) { + if (e.target === iconContainer.querySelector('ha-icon')) { + isDragging = false; + iconContainer.addEventListener('click', event => { + fireEvent(this, "hass-more-info", { + entityId: entityId + }); + }); } else { - toggleEntity(entityId); + isDragging = true; + updateRange(e.pageX || e.touches[0].pageX); + document.addEventListener('mouseup', handleEnd); + document.addEventListener('touchend', handleEnd); + document.addEventListener('mousemove', handleMove); + document.addEventListener('touchmove', handleMove); } - }); - //switchButton.addEventListener('touchend', () => toggleEntity(entityId)); - this.eventAdded = true; - } - else if (!this.eventAdded && buttonType === 'slider') { - rangeSlider.addEventListener('mousedown', handleStart); - rangeSlider.addEventListener('touchstart', handleStart); - this.eventAdded = true; - } - - if (!isDragging && buttonType === 'slider') { - this.rangeFill.style.transition = 'all .2s'; - if (entityId.startsWith("light.")) { - this.rangeFill.style.transform = `translateX(${(currentBrightness / 255) * 100}%)`; - } - else if (entityId.startsWith("media_player.")) { - this.rangeFill.style.transform = `translateX(${currentVolume * 100}%)`; - } - } - - function updateButtonStyle(state, content) { - content.buttonContainer.style.opacity = state !== 'unavailable' ? '1' : '0.5'; - if (buttonType === 'switch') { - const backgroundColor = state === 'on' || state === 'open' || state === 'cleaning' ? 'var(--accent-color)' : 'rgba(0,0,0,0)'; - content.switchButton.style.backgroundColor = backgroundColor; } - } + + function handleEnd() { + isDragging = false; + if (entityId.startsWith("light.")) { //currentBrightness !== brightness && + currentBrightness = brightness; + hass.callService('light', 'turn_on', { + entity_id: entityId, + brightness: currentBrightness + }); + } else if (currentVolume !== volume && entityId.startsWith("media_player.")) { + currentVolume = volume; + hass.callService('media_player', 'volume_set', { + entity_id: entityId, + volume_level: currentVolume + }); + } - updateButtonStyle(state, this); - - function updateRange(x) { - const rect = rangeSlider.getBoundingClientRect(); - const position = Math.min(Math.max(x - rect.left, 0), rect.width); - const percentage = position / rect.width; - if (entityId.startsWith("light.")) { - brightness = Math.round(percentage * 255); - } - else if (entityId.startsWith("media_player.")) { - volume = percentage; - } - - rangeFill.style.transition = 'none'; - rangeFill.style.transform = `translateX(${percentage * 100}%)`; - } - - if (buttonType === 'slider') { - const rgbColor = hass.states[entityId].attributes.rgb_color; - const rgbColorOpacity = rgbColor ? `rgba(${rgbColor[0]}, ${rgbColor[1]}, ${rgbColor[2]}, 0.5)` : `rgba(255, 255, 255, 0.5)`; - rangeFill.style.backgroundColor = rgbColorOpacity; - this.rangeFill.style.backgroundColor = rgbColorOpacity; - } - - if (!this.styleAdded) { - const styleElement = document.createElement('style'); - const styles = ` - ha-card { - margin-top: 0 !important; - background: none !important; - } - - .button-container { - position: relative; - width: 100%; - height: 50px; - z-index: 0; - background-color: var(--background-color-2,var(--secondary-background-color)); - border-radius: 25px; - mask-image: radial-gradient(white, black); - -webkit-mask-image: radial-gradient(white, black); - -webkit-backface-visibility: hidden; - -moz-backface-visibility: hidden; - -webkit-transform: translateZ(0); - overflow: hidden; + document.removeEventListener('mouseup', handleEnd); + document.removeEventListener('touchend', handleEnd); + document.removeEventListener('mousemove', handleMove); + document.removeEventListener('touchmove', handleMove); } - - .switch-button, - .range-slider { - display: inline-flex; - position: absolute; - height: 100%; - width: 100%; - transition: background-color 1.5s; - } - - .range-fill { - position: absolute; - top: 0; - bottom: 0; - left: 0; - } - - .switch-button { - cursor: pointer !important; + + function handleMove(e) { + if (isDragging) { + updateRange(e.pageX || e.touches[0].pageX); + } } - - .range-slider { - cursor: ew-resize; + + if (!this.eventAdded && buttonType === 'switch') { + switchButton.addEventListener('click', () => tapFeedback(this.switchButton)); + //switchButton.addEventListener('touchstart', () => tapFeedback(this.switchButton)); + switchButton.addEventListener('click', function(e) { + if (e.target.closest('ha-icon')) { //e.target === this.querySelector('ha-icon') + fireEvent(this, "hass-more-info", { + entityId: entityId + }); + } else { + toggleEntity(entityId); + } + }); + //switchButton.addEventListener('touchend', () => toggleEntity(entityId)); + this.eventAdded = true; + } else if (!this.eventAdded && buttonType === 'slider') { + rangeSlider.addEventListener('mousedown', handleStart); + rangeSlider.addEventListener('touchstart', handleStart); + this.eventAdded = true; } - - .range-fill { - z-index: 1; - width: 100%; - left: -100%; + + if (!this.isDragging && buttonType === 'slider') { + this.rangeFill.style.transition = 'all .2s'; + if (entityId.startsWith("light.")) { + this.rangeFill.style.transform = `translateX(${(currentBrightness / 255) * 100}%)`; + } else if (entityId.startsWith("media_player.")) { + this.rangeFill.style.transform = `translateX(${currentVolume * 100}%)`; + } } - - .icon-container { - position: absolute; - z-index: 2; - width: 38px; - height: 38px; - margin: 6px; - border-radius: 50%; - background-color: var(--card-background-color,var(--ha-card-background)); + + function updateButtonStyle(state, content) { + content.buttonContainer.style.opacity = state !== 'unavailable' ? '1' : '0.5'; + if (buttonType === 'switch') { + const backgroundColor = state === ('on' || 'open' || 'cleaning' || 'true') ? 'var(--accent-color)' : 'rgba(0,0,0,0)'; + content.switchButton.style.backgroundColor = backgroundColor; + } } - - ha-icon { - display: flex; - position: absolute; - margin: inherit; - padding: 1px 2px; - width: 22px; - height: 22px; - cursor: pointer !important; + + updateButtonStyle(state, this); + + function updateRange(x) { + const rect = rangeSlider.getBoundingClientRect(); + const position = Math.min(Math.max(x - rect.left, 0), rect.width); + const percentage = position / rect.width; + if (entityId.startsWith("light.")) { + brightness = Math.round(percentage * 255); + } else if (entityId.startsWith("media_player.")) { + volume = percentage; + } + + rangeFill.style.transition = 'none'; + rangeFill.style.transform = `translateX(${percentage * 100}%)`; } - - .nameContainer { - position: relative; - display: inline-flex; - margin-left: 58px; - z-index: 2; - font-weight: 600; - align-items: center; + + if (buttonType === 'slider') { + const rgbColor = hass.states[entityId].attributes.rgb_color; + const rgbColorOpacity = rgbColor ? `rgba(${rgbColor[0]}, ${rgbColor[1]}, ${rgbColor[2]}, 0.5)` : `rgba(255, 255, 255, 0.5)`; + rangeFill.style.backgroundColor = rgbColorOpacity; + this.rangeFill.style.backgroundColor = rgbColorOpacity; } + + if (!this.styleAdded) { + const styleElement = document.createElement('style'); + const styles = ` + ha-card { + margin-top: 0 !important; + background: none !important; + } + + .button-container { + position: relative; + width: 100%; + height: 50px; + z-index: 0; + background-color: var(--background-color-2,var(--secondary-background-color)); + border-radius: 25px; + mask-image: radial-gradient(white, black); + -webkit-mask-image: radial-gradient(white, black); + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + -webkit-transform: translateZ(0); + overflow: hidden; + } + + .switch-button, + .range-slider { + display: inline-flex; + position: absolute; + height: 100%; + width: 100%; + transition: background-color 1.5s; + } - .nameContainer p { - display: inline-flex; + .range-fill { + position: absolute; + top: 0; + bottom: 0; + left: 0; + } + + .switch-button { + cursor: pointer !important; + } + + .range-slider { + cursor: ew-resize; + } + + .range-fill { + z-index: 1; + width: 100%; + left: -100%; + } + + .icon-container { + position: absolute; + z-index: 2; + width: 38px; + height: 38px; + margin: 6px; + border-radius: 50%; + background-color: var(--card-background-color,var(--ha-card-background)); + } + + ha-icon { + display: flex; + position: absolute; + margin: inherit; + padding: 1px 2px; + width: 22px; + height: 22px; + cursor: pointer !important; + } + + .nameContainer { + position: relative; + display: inline-flex; + margin-left: 58px; + z-index: 2; + font-weight: 600; + align-items: center; + } + + .nameContainer p { + display: inline-flex; + } + + .feedback-element { + position: absolute; + top: 0; + left: 0; + opacity: 0; + width: 100%; + height: 100%; + background-color: rgb(0,0,0); + } + + @keyframes tap-feedback { + 0% {transform: translateX(-100%); opacity: 0;} + 64% {transform: translateX(0); opacity: 0.1;} + 100% {transform: translateX(100%); opacity: 0;} + } + `; + + styleElement.innerHTML = styles; + this.content.appendChild(styleElement); + this.styleAdded = true; } - - .feedback-element { - position: absolute; - top: 0; - left: 0; - opacity: 0; - width: 100%; - height: 100%; - background-color: rgb(0,0,0); + } + + // Initialize separator + if (this.config.card_type === 'separator') { + const icon = !this.config.icon ? '' : this.config.icon; + const name = !this.config.name ? '' : this.config.name; + + if (!this.separatorAdded) { + const separatorContainer = document.createElement("div"); + separatorContainer.setAttribute("class", "separator-container"); + separatorContainer.innerHTML = ` +${name}
+${name}
+ + // Intitalize empty card + if (this.config.card_type === 'empty-column') { + if (!this.emptyCollumnAdded) { + const separatorContainer = document.createElement("div"); + separatorContainer.setAttribute("class", "empty-column"); + separatorContainer.innerHTML = ` +