diff --git a/src/bubble-card.ts b/src/bubble-card.ts deleted file mode 100644 index cb11d68e..00000000 --- a/src/bubble-card.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { version } from './var/version.ts'; -import { initializeContent } from './tools/init.ts'; -import { handlePopUp } from './cards/pop-up/index.ts'; -import { handleHorizontalButtonsStack } from './cards/horizontal-buttons-stack/index.ts'; -import { handleButton } from './cards/button/index.ts'; -import { handleSeparator } from './cards/separator/index.ts'; -import { handleCover } from './cards/cover/index.ts'; -import { handleEmptyColumn } from './cards/empty-column/index.ts'; -import { handleMediaPlayer } from './cards/media-player/index.ts'; -import { createBubbleCardEditor } from './editor/bubble-card-editor.ts'; - -class BubbleCard extends HTMLElement { - editor = false; - isConnected = false; - - connectedCallback() { - this.isConnected = true; - - if (this._hass) { - this.updateBubbleCard(); - } - } - - disconnectedCallback() { - this.isConnected = false; - } - - set editMode(editMode) { - if (this.editor === editMode) { - return; - } - - this.editor = editMode; - - if (this._hass) { - this.updateBubbleCard(); - } - } - - set hass(hass) { - initializeContent(this); - - this._hass = hass; - - if (this.isConnected || this.config.card_type === 'pop-up') { - this.updateBubbleCard(); - } - - if (!window.columnFix) { - window.columnFix = this.config.column_fix - } - } - - updateBubbleCard() { - - switch (this.config.card_type) { - - // Update pop-up card - case 'pop-up': - handlePopUp(this); - break; - - // Update button - case 'button' : - handleButton(this); - break; - - // Update separator - case 'separator' : - handleSeparator(this); - break; - - // Update cover card - case 'cover' : - handleCover(this); - break; - - // Update empty card - case 'empty-column' : - handleEmptyColumn(this); - break; - - // Update horizontal buttons stack - case 'horizontal-buttons-stack' : - handleHorizontalButtonsStack(this); - break; - - // Update media player - case 'media-player' : - handleMediaPlayer(this); - break; - } - } - - setConfig(config) { - if (config.card_type === 'pop-up') { - if (!config.hash) { - throw new Error("You need to define an hash. Please note that this card must be placed inside a vertical_stack to work as a pop-up."); - } - } else if (config.card_type === 'horizontal-buttons-stack') { - var definedLinks = {}; - - for (var key in config) { - if (key.match(/^\d+_icon$/)) { - var iconKey = key; - var linkKey = key.replace('_icon', '_link'); - - if (config[linkKey] === undefined) { - throw new Error("You need to define " + linkKey); - } - - if (definedLinks[config[linkKey]]) { - throw new Error("You can't use " + config[linkKey] + " twice" ); - } - - definedLinks[config[linkKey]] = true; - } - } - } else if (config.card_type === 'button' || config.card_type === 'cover') { - if (!config.entity && config.button_type !== 'name') { - throw new Error("You need to define an entity"); - } - } - - if (window.entityError) { - throw new Error("You need to define a valid entity"); - } - if (config.card_type === 'button') { - const enhancedConfig = {...config}; - const buttonType = enhancedConfig.button_type || 'switch'; - - enhancedConfig.tap_action = enhancedConfig.tap_action ?? { - action: "more-info" - }; - enhancedConfig.double_tap_action = enhancedConfig.double_tap_action ?? { - action: buttonType === "state" ? "more-info" : "toggle" - } - enhancedConfig.hold_action = enhancedConfig.hold_action ?? { - action: buttonType === "state" ? "more-info" : "toggle" - } - - this.config = enhancedConfig; - } else { - this.config = config; - } - - if (this._hass) { - this.updateBubbleCard(); - } - } - - getCardSize() { - switch (this.config.card_type) { - case 'pop-up': - return -100000; - case 'button': - return 1; - case 'separator': - return 1; - case 'cover': - return 2; - case 'empty-column': - return 1; - case 'horizontal-buttons-stack': - return 0; - case 'media-player': - return 1; - } - } - - static getConfigElement() { - createBubbleCardEditor(); - return document.createElement("bubble-card-editor"); - } - - getLayoutOptions() { - let defaultRows = 1; - if (['popup', 'horizontal-buttons-stack'].includes(this.config.card_type)) { - defaultRows = 0; - } else if (['cover'].includes(this.config.card_type)) { - defaultRows = 2; - } - - let defaultColumns = 4; - if (['popup', 'horizontal-buttons-stack'].includes(this.config.card_type)) { - defaultColumns = 0; - } - - return { - grid_columns: this.config.columns ?? defaultColumns, - grid_rows: this.config.rows ?? defaultRows, - } - } -} - -customElements.define("bubble-card", BubbleCard); - -window.customCards = window.customCards || []; -window.customCards.push({ - type: "bubble-card", - name: "Bubble Card", - preview: false, - description: "A minimalist card collection with a nice pop-up touch.", - documentationURL: "https://github.com/Clooos/Bubble-Card/" -}); - -console.info( - `%c Bubble Card %c ${version} `, - 'background-color: #555;color: #fff;padding: 3px 2px 3px 3px;border-radius: 14px 0 0 14px;font-family: DejaVu Sans,Verdana,Geneva,sans-serif;text-shadow: 0 1px 0 rgba(1, 1, 1, 0.3)', - 'background-color: #506eac;color: #fff;padding: 3px 3px 3px 2px;border-radius: 0 14px 14px 0;font-family: DejaVu Sans,Verdana,Geneva,sans-serif;text-shadow: 0 1px 0 rgba(1, 1, 1, 0.3)' -); \ No newline at end of file diff --git a/src/cards/button/changes.ts b/src/cards/button/changes.ts deleted file mode 100644 index 75d76e1a..00000000 --- a/src/cards/button/changes.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { getButtonType } from "./helpers.ts"; -import { initializesubButtonIcon } from '../../tools/global-changes.ts'; -import { - applyScrollingEffect, - getIcon, - getIconColor, - getImage, - getName, - getState, - getAttribute, - isStateOn, - isEntityType, - getWeatherIcon, - setLayout -} from '../../tools/utils.ts'; - -export function changeButton(context) { - const buttonType = getButtonType(context); - const isLight = isEntityType(context, "light"); - const isOn = isStateOn(context); - const lightColor = getIconColor(context); - - if (buttonType === 'switch' && isOn) { - if (lightColor && isLight) { - context.card.style.setProperty('--bubble-button-background-color', getIconColor(context)); - context.elements.buttonBackground.style.opacity = '.5'; - } else { - context.card.style.setProperty('--bubble-button-background-color', 'var(--accent-color)'); - context.elements.buttonBackground.style.opacity = '1'; - } - } else { - context.card.style.setProperty('--bubble-button-background-color', 'rgba(0, 0, 0, 0)'); - context.elements.buttonBackground.style.opacity = '.5'; - } -} -export function changeIcon(context) { - const buttonType = getButtonType(context); - const isOn = buttonType !== 'name' ? isStateOn(context) : false; - const icon = buttonType !== 'name' ? getIcon(context) : context.config.icon; - const image = buttonType !== 'name' ? getImage(context) : ''; - const isLight = buttonType !== 'name' ? isEntityType(context, "light") : false; - - if (isLight && isOn) { - context.elements.iconContainer.style.color = getIconColor(context); - } else { - context.elements.iconContainer.style.color = ''; - } - - if (image !== '') { - context.elements.image.style.backgroundImage = 'url(' + image + ')'; - context.elements.icon.style.display = 'none'; - context.elements.image.style.display = ''; - } else if (icon !== '') { - context.elements.icon.icon = icon; - context.elements.icon.style.color = isOn && buttonType !== 'state' ? getIconColor(context) : 'inherit'; - context.elements.icon.style.display = ''; - context.elements.image.style.display = 'none'; - } else { - context.elements.icon.style.display = 'none'; - context.elements.image.style.display = 'none'; - } -} -export function changeName(context) { - const buttonType = getButtonType(context); - const name = buttonType !== 'name' ? getName(context) : context.config.name; - if (name !== context.elements.previousName) { - applyScrollingEffect(context, context.elements.name, name); - context.elements.previousName = name; - } -} -export function changeSlider(context) { - const buttonType = getButtonType(context); - - if (buttonType === 'slider') { - context.elements.rangeFill.style.backgroundColor = getIconColor(context); - - if (context.dragging) return; - - let percentage = 0; - - if (isEntityType(context, "light")) { - percentage = 100 * getAttribute(context, "brightness") / 255; - } else if (isEntityType(context, "media_player")) { - percentage = 100 * getAttribute(context, "volume_level"); - } else if (isEntityType(context, "cover")) { - percentage = getAttribute(context, "current_position"); - } else if (isEntityType(context, "input_number")) { - const minValue = getAttribute(context, "min"); - const maxValue = getAttribute(context, "max"); - const value = getState(context); - percentage = 100 * (value - minValue) / (maxValue - minValue); - } - - context.elements.rangeFill.style.transform = `translateX(${percentage}%)`; - } -} - -export function changeStatus(context) { - const state = getState(context); - - if (state === 'unavailable') { - context.card.classList.add('is-unavailable'); - } else { - context.card.classList.remove('is-unavailable'); - } - - if (isEntityType(context, "light")) { - context.card.classList.add('is-light'); - } else { - context.card.classList.remove('is-light'); - } - - if (isStateOn(context)) { - context.card.classList.add('is-on'); - } else { - context.card.classList.remove('is-on'); - } -} - -export function changeStyle(context) { - initializesubButtonIcon(context); - setLayout(context); - - const state = getState(context); - - const customStyle = context.config.styles - ? Function('hass', 'entityId', 'state', 'icon', 'subButtonIcon', 'getWeatherIcon', `return \`${context.config.styles}\`;`) - (context._hass, context.config.entity, state, context.elements.icon.icon, context.subButtonIcon, getWeatherIcon) - : ''; - - context.elements.customStyle.innerText = customStyle; -} diff --git a/src/cards/button/create.ts b/src/cards/button/create.ts deleted file mode 100644 index b946d3f0..00000000 --- a/src/cards/button/create.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { addActions, addFeedback } from "../../tools/tap-actions.ts"; -import { createElement, toggleEntity } from "../../tools/utils.ts"; -import { getButtonType, onSliderChange } from "./helpers.ts"; -import styles from "./styles.ts"; - -export function createStructure(context, container = context.content, appendTo = container) { - const buttonType = getButtonType(context); - - context.dragging = false; - - if (!context.elements) context.elements = {}; - - context.elements.buttonCardContainer = createElement('div', 'bubble-button-card-container button-container'); - context.elements.buttonCard = createElement('div', 'bubble-button-card switch-button'); - context.elements.buttonBackground = createElement('div', 'bubble-button-background'); - context.elements.nameContainer = createElement('div', 'bubble-name-container name-container'); - context.elements.iconContainer = createElement('div', 'bubble-icon-container icon-container'); - context.elements.name = createElement('div', 'bubble-name name'); - context.elements.state = createElement('div', 'bubble-state state'); - context.elements.feedback = createElement('div', 'bubble-feedback-element feedback-element'); - context.elements.icon = createElement('ha-icon', 'bubble-icon icon'); - context.elements.image = createElement('div', 'bubble-entity-picture entity-picture'); - context.elements.style = createElement('style'); - context.elements.customStyle = createElement('style'); - - context.elements.feedback.style.display = 'none'; - context.elements.style.innerText = styles; - - context.elements.iconContainer.appendChild(context.elements.icon); - context.elements.iconContainer.appendChild(context.elements.image) - - context.elements.nameContainer.appendChild(context.elements.name); - if (buttonType !== "name") { - context.elements.nameContainer.appendChild(context.elements.state); - } - - context.elements.buttonCard.appendChild(context.elements.buttonBackground); - context.elements.buttonCard.appendChild(context.elements.iconContainer); - context.elements.buttonCard.appendChild(context.elements.nameContainer); - context.elements.buttonCard.appendChild(context.elements.feedback); - context.elements.buttonCardContainer.appendChild(context.elements.buttonCard); - - container.innerHTML = ''; - - if (appendTo === container) { - container.appendChild(context.elements.buttonCardContainer); - } - - container.appendChild(context.elements.style); - container.appendChild(context.elements.customStyle); - - if (appendTo !== container) { - appendTo.innerHTML = ''; - context.elements.buttonCardContainer.appendChild(container); - appendTo.appendChild(context.elements.buttonCardContainer); - context.buttonType = buttonType; - } else { - context.cardType = `button-${buttonType}`; - } -} -export function createSwitchStructure(context) { - addActions(context.elements.iconContainer, context.config); - - const switchDefaultActions = { - tap_action: { action: "toggle" }, - double_tap_action: { action: "toggle" }, - hold_action: { action: "more-info" } - }; - addActions(context.elements.buttonBackground, context.config.button_action, context.config.entity, switchDefaultActions); - addFeedback(context.elements.buttonBackground, context.elements.feedback); -} -export function createNameStructure(context) { - const nameDefaultActions = { - tap_action: { action: "none" }, - double_tap_action: { action: "none" }, - hold_action: { action: "none" } - }; - - context.elements.buttonCard.style.cursor = 'auto'; - - addActions(context.elements.iconContainer, context.config, context.config.entity, nameDefaultActions); - addFeedback(context.elements.buttonBackground, context.elements.feedback); -} -export function createStateStructure(context) { - addActions(context.elements.buttonCardContainer, context.config); - addFeedback(context.elements.buttonBackground, context.elements.feedback); -} -export function createSliderStructure(context) { - addActions(context.elements.iconContainer, context.config); - - let initialX = 0; - - context.elements.rangeFill = createElement('div', 'bubble-range-fill range-fill'); - context.elements.rangeSlider = createElement('div', 'bubble-range-slider range-slider'); - context.elements.rangeSlider.appendChild(context.elements.rangeFill); - context.elements.buttonCardContainer.appendChild(context.elements.rangeSlider); - - context.elements.buttonCardContainer.addEventListener('pointercancel', onPointerCancel); - context.elements.buttonCardContainer.addEventListener('pointerdown', (e) => { - context.elements.buttonCardContainer.setPointerCapture(e.pointerId); - - if (context.card.classList.contains('is-unavailable')) { - return; - } - - context.dragging = true; - initialX = e.pageX || (e.touches ? e.touches[0].pageX : 0); - - context.elements.buttonCardContainer.classList.add('is-dragging'); - context.elements.buttonCardContainer.addEventListener('pointermove', onPointerMove); - window.addEventListener('pointerup', onPointerUp); - }); - - function onPointerCancel() { - context.dragging = false; - - context.elements.buttonCardContainer.classList.remove('is-dragging'); - context.elements.buttonCardContainer.removeEventListener('pointermove', onPointerMove); - window.removeEventListener('pointerup', onPointerUp); - } - - function onPointerMove(e) { - e.stopPropagation(); - - const moveX = e.pageX || (e.touches ? e.touches[0].pageX : 0); - if (Math.abs(initialX-moveX) > 10) { - onSliderChange(context, moveX, true); - } - - if (!context.dragging) { - onSliderChange(context, moveX); - } - } - function onPointerUp(e) { - e.stopPropagation(); - - context.dragging = false; - - const moveX = e.pageX || (e.touches ? e.touches[0].pageX : 0); - onSliderChange(context, moveX); - - context.elements.buttonCardContainer.classList.remove('is-dragging'); - context.elements.buttonCardContainer.removeEventListener('pointermove', onPointerMove); - window.removeEventListener('pointerup', onPointerUp); - } -} - diff --git a/src/cards/button/helpers.ts b/src/cards/button/helpers.ts deleted file mode 100644 index 814a5b1a..00000000 --- a/src/cards/button/helpers.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { throttle, isEntityType, getAttribute } from "../../tools/utils.ts"; - -export function getButtonType(context) { - let buttonType = context.config.button_type; - - if (buttonType === 'custom') { - console.error('Buttons "custom" have been removed. Use either "switch", "slider", "state" or "name"'); - buttonType = ''; - } - - if (context.config.entity) { - return buttonType || 'switch'; - } else { - return buttonType || 'name'; - } -} - -export function updateEntity(context, value) { - if (isEntityType(context, "light")) { - context._hass.callService('light', 'turn_on', { - entity_id: context.config.entity, - brightness: 255 * value / 100 - }); - } else if (isEntityType(context, "media_player")) { - context._hass.callService('media_player', 'volume_set', { - entity_id: context.config.entity, - volume_level: value / 100 - }); - } else if (isEntityType(context, "cover")) { - context._hass.callService('cover', 'set_cover_position', { - entity_id: context.config.entity, - position: value - }); - } else if (isEntityType(context, "input_number")) { - const minValue = getAttribute(context, "min"); - const maxValue = getAttribute(context, "max"); - context._hass.callService('input_number', 'set_value', { - entity_id: context.config.entity, - value: Math.round((maxValue - minValue) * value / 100 + minValue) - }); - } -}; -export const throttledUpdateEntity = throttle(updateEntity); - -export function onSliderChange(context, leftDistance, throttle = false) { - const rect = context.elements.rangeSlider.getBoundingClientRect(); - const percentage = 100 * (leftDistance - rect.left) / rect.width; - const rangedPercentage = Math.min(100, Math.max(0, percentage)); - - context.elements.rangeFill.style.transform =`translateX(${rangedPercentage}%)`; - if (throttle) { - throttledUpdateEntity(context, rangedPercentage); - } else { - updateEntity(context, rangedPercentage); - } -} diff --git a/src/cards/button/index.ts b/src/cards/button/index.ts deleted file mode 100644 index 31a7609b..00000000 --- a/src/cards/button/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { changeState, changeSubButtonState } from "../../tools/global-changes.ts"; -import { - changeStatus, - changeButton, - changeName, - changeIcon, - changeSlider, - changeStyle -} from './changes.ts' -import { getButtonType } from './helpers.ts'; -import { - createNameStructure, - createSliderStructure, - createStateStructure, - createStructure, - createSwitchStructure -} from './create.ts'; - -export function handleButton(context, container = context.content, appendTo = container) { - const buttonType = getButtonType(context); - if (context.cardType !== `button-${buttonType}` && context.buttonType !== buttonType) { - createStructure(context, container, appendTo); - - if (buttonType === 'switch') { - createSwitchStructure(context); - } else if (buttonType === 'slider') { - createSliderStructure(context); - } else if (buttonType === 'state') { - createStateStructure(context); - } else if (buttonType === 'name') { - createNameStructure(context); - } - } - - if (buttonType !== 'name') { - changeStatus(context); - changeButton(context); - changeSlider(context); - } - - changeIcon(context); - changeName(context); - changeState(context); - changeSubButtonState(context, container, appendTo.firstChild.firstChild); - changeStyle(context); -} - diff --git a/src/cards/button/styles.ts b/src/cards/button/styles.ts deleted file mode 100644 index 24c44b8c..00000000 --- a/src/cards/button/styles.ts +++ /dev/null @@ -1,193 +0,0 @@ -export default ` - * { - -webkit-tap-highlight-color: transparent !important; - } - ha-card { - margin-top: 0; - background: none; - opacity: 1; - } - .is-unavailable { - opacity: 0.5; - } - - .bubble-button-card-container { - position: relative; - width: 100%; - height: 50px; - background-color: var(--background-color-2,var(--secondary-background-color)); - border-radius: 25px; - mask-image: radial-gradient(white, black); - -webkit-transform: translateZ(0); - overflow: hidden; - touch-action: pan-y; - } - - .bubble-button-card, - .bubble-range-slider, - .bubble-button-background { - display: flex; - position: absolute; - justify-content: space-between; - align-items: center; - height: 100%; - width: 100%; - transition: background-color 1.5s; - background-color: var(--bubble-button-background-color); - } - .bubble-button-background { - opacity: .5; - } - .bubble-range-fill { - z-index: -1; - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - left: -100%; - transition: all .3s; - } - .is-dragging .bubble-range-fill { - transition: none; - } - .is-light .bubble-range-fill, - .bubble-button-background { - opacity: 0.5; - } - - .bubble-button-card { - cursor: pointer; - } - .is-unavailable .bubble-button-card, - .is-unavailable .bubble-range-slider { - cursor: not-allowed; - } - .bubble-range-slider { - cursor: ew-resize; - } - .bubble-icon-container { - display: flex; - flex-wrap: wrap; - align-content: center; - justify-content: center; - min-width: 38px; - min-height: 38px; - margin: 6px; - border-radius: 50%; - background-color: var(--card-background-color, var(--ha-card-background)); - overflow: hidden; - z-index: 1; - position: relative; - cursor: pointer; - } - .bubble-icon-container::after { - content: ''; - background-color: currentColor; - position: absolute; - display: block; - width: 100%; - height: 100%; - transition: all 1s; - left: 0; - right: 0; - opacity: 0; - } - .is-light.is-on .bubble-icon-container::after { - opacity: 0.2; - } - .is-unavailable.is-light .bubble-icon-container::after { - opacity: 0; - } - - .bubble-icon { - display: flex; - opacity: 0.6; - } - - .is-on .bubble-icon { - filter: brightness(1.1); - opacity: 1; - } - - .bubble-entity-picture { - background-size: cover; - background-position: center; - height: 100%; - width: 100%; - position: absolute; - } - - .bubble-name, - .bubble-state { - display: flex; - margin: 2px 0; - position: relative; - white-space: nowrap; - } - - .bubble-name-container { - display: flex; - line-height: 14px; - flex-direction: column; - justify-content: center; - flex-grow: 1; - margin: 0 16px 0 4px; - pointer-events: none; - position: relative; - overflow: hidden; - } - - .bubble-name { - font-weight: 600; - } - - .bubble-state { - font-size: 12px; - opacity: 0.7; - font-weight: normal; - } - - .bubble-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;} - } - - .large .bubble-button-card-container { - height: 64px; - border-radius: 32px; - } - - .large .bubble-icon-container { - --mdc-icon-size: 28px; - min-width: 48px !important; - min-height: 48px !important; - margin-left: 8px; - } - - .rows-2 .bubble-sub-button-container { - flex-direction: column; - gap: 4px !important; - display: grid !important; - grid-template-columns: repeat(2, min-content); - grid-template-rows: repeat(2, 1fr); - grid-auto-flow: column; - width: auto; - padding-right: 14px; - } - - .rows-2 .bubble-sub-button { - height: 20px !important; - } -`; \ No newline at end of file diff --git a/src/cards/cover/changes.ts b/src/cards/cover/changes.ts deleted file mode 100644 index a5405af2..00000000 --- a/src/cards/cover/changes.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - getName, - getState, - getAttribute, - getIcon, - applyScrollingEffect, - getWeatherIcon, - setLayout -} from "../../tools/utils.ts"; -import { initializesubButtonIcon } from '../../tools/global-changes.ts'; - -export function changeIcon(context) { - const iconOpen = context.config.icon_open; - const iconClosed = context.config.icon_close; - const isOpen = getState(context) !== 'closed'; - const isCurtains = getAttribute(context, 'device_class') === 'curtain'; - - context.elements.icon.icon = isOpen ? - getIcon(context, context.config.entity, context.config.icon_open) : - getIcon(context, context.config.entity, context.config.icon_close); - context.elements.iconOpen.icon = context.config.icon_up || (isCurtains ? "mdi:arrow-expand-horizontal" : "mdi:arrow-up"); - context.elements.iconClose.icon = context.config.icon_down || (isCurtains ? "mdi:arrow-collapse-horizontal" : "mdi:arrow-down"); -} -export function changeName(context) { - const name = getName(context); - if (name !== context.elements.previousName) { - context.elements.name.innerText = name; - applyScrollingEffect(context, context.elements.name, name); - context.elements.previousName = name; - } -} -export function changeStyle(context) { - initializesubButtonIcon(context); - setLayout(context); - - const state = getState(context); - - const customStyle = context.config.styles - ? Function('hass', 'entityId', 'state', 'icon', 'subButtonIcon', 'getWeatherIcon', `return \`${context.config.styles}\`;`) - (context._hass, context.config.entity, state, context.elements.icon.icon, context.subButtonIcon, getWeatherIcon) - : ''; - - context.elements.customStyle.innerText = customStyle; -} - - - diff --git a/src/cards/cover/create.ts b/src/cards/cover/create.ts deleted file mode 100644 index 1128a0bf..00000000 --- a/src/cards/cover/create.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { addActions } from "../../tools/tap-actions.ts"; -import { createElement } from "../../tools/utils.ts"; -import styles from "./styles.ts"; - -export function createStructure(context) { - context.elements = {}; - context.elements.coverCardContainer = createElement('div', 'bubble-cover-card-container cover-container'); - context.elements.headerContainer = createElement('div', 'bubble-header header-container'); - context.elements.buttonsContainer = createElement('div', 'bubble-buttons buttons-container'); - context.elements.iconContainer = createElement('div', 'bubble-icon-container icon-container'); - context.elements.icon = createElement('ha-icon', 'bubble-icon'); - context.elements.nameContainer = createElement('div', 'bubble-name-container name-container'); - context.elements.name = createElement('div', 'bubble-name name'); - context.elements.state = createElement('div', 'bubble-state state'); - context.elements.buttonOpen = createElement('div', 'bubble-button bubble-open button open'); - context.elements.buttonStop = createElement('div', 'bubble-button bubble-stop button stop'); - context.elements.buttonClose = createElement('div', 'bubble-button bubble-close button close'); - context.elements.iconOpen = createElement('ha-icon', 'bubble-icon bubble-icon-open'); - context.elements.iconStop = createElement('ha-icon', 'bubble-icon bubble-icon-stop'); - context.elements.iconStop.icon = 'mdi:stop'; - context.elements.iconClose = createElement('ha-icon', 'bubble-icon bubble-icon-close'); - - context.elements.style = createElement('style'); - context.elements.style.innerText = styles; - context.elements.customStyle = createElement('style'); - - context.elements.iconContainer.appendChild(context.elements.icon); - context.elements.headerContainer.appendChild(context.elements.iconContainer); - context.elements.headerContainer.appendChild(context.elements.nameContainer); - context.elements.nameContainer.appendChild(context.elements.name); - context.elements.nameContainer.appendChild(context.elements.state); - context.elements.buttonsContainer.appendChild(context.elements.buttonOpen); - context.elements.buttonsContainer.appendChild(context.elements.buttonStop); - context.elements.buttonsContainer.appendChild(context.elements.buttonClose); - - context.elements.buttonOpen.appendChild(context.elements.iconOpen); - context.elements.buttonOpen.addEventListener('click', () => { - const openCover = context.config.open_service ?? 'cover.open_cover'; - const [domain, action] = openCover.split('.'); - context._hass.callService(domain, action, { - entity_id: context.config.entity - }); - }); - - context.elements.buttonStop.appendChild(context.elements.iconStop); - context.elements.buttonStop.addEventListener('click', () => { - const stopCover = context.config.stop_service ?? 'cover.stop_cover'; - const [domain, action] = stopCover.split('.'); - context._hass.callService(domain, action, { - entity_id: context.config.entity - }); - }); - - context.elements.buttonClose.appendChild(context.elements.iconClose); - context.elements.buttonClose.addEventListener('click', () => { - const closeCover = context.config.close_service ?? 'cover.close_cover'; - const [domain, action] = closeCover.split('.'); - context._hass.callService(domain, action, { - entity_id: context.config.entity - }); - }); - - addActions(context.elements.iconContainer, context.config); - - context.content.innerHTML = ''; - - context.content.appendChild(context.elements.coverCardContainer); - context.content.appendChild(context.elements.style); - context.content.appendChild(context.elements.customStyle); - - context.elements.coverCardContainer.appendChild(context.elements.headerContainer); - context.elements.coverCardContainer.appendChild(context.elements.buttonsContainer); - - context.cardType = "cover"; -} \ No newline at end of file diff --git a/src/cards/cover/index.ts b/src/cards/cover/index.ts deleted file mode 100644 index d5e67c22..00000000 --- a/src/cards/cover/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { changeState, changeSubButtonState } from "../../tools/global-changes.ts"; -import { - changeIcon, - changeName, - changeStyle, -} from './changes.ts' -import { createStructure } from './create.ts'; - -export function handleCover(context) { - if (context.cardType !== "cover") { - createStructure(context); - } - - changeIcon(context); - changeName(context); - changeState(context); - changeSubButtonState(context, context.content, context.elements.headerContainer); - changeStyle(context); -} diff --git a/src/cards/cover/styles.ts b/src/cards/cover/styles.ts deleted file mode 100644 index 61298c7a..00000000 --- a/src/cards/cover/styles.ts +++ /dev/null @@ -1,143 +0,0 @@ -export default ` - * { - -webkit-tap-highlight-color: transparent !important; - } - ha-card { - margin-top: 0 !important; - background: none !important; - } - - .bubble-cover-card-container { - display: grid; - gap: 10px; - } - - .bubble-header { - display: flex; - align-items: center; - overflow: hidden; - } - - .bubble-icon-container { - display: flex; - flex-wrap: wrap; - align-content: center; - justify-content: center; - min-width: 38px; - min-height: 38px; - border-radius: 50%; - background-color: var(--card-background-color, var(--ha-card-background)); - border: 6px solid var(--background-color-2, var(--secondary-background-color)); - cursor: pointer; - } - - .bubble-name-container { - display: flex; - line-height: 1em; - flex-direction: column; - justify-content: center; - flex-grow: 1; - font-weight: 600; - margin-left: 4px; - margin-right: 16px; - pointer-events: none; - position: relative; - overflow: hidden; - } - - .bubble-name { - margin: 2px 0; - white-space: nowrap; - display: flex; - position: relative; - } - - .bubble-state { - font-size: 12px; - opacity: 0.7; - margin: 2px 0; - font-weight: normal; - white-space: nowrap; - display: flex; - position: relative; - } - - .bubble-buttons { - display: grid; - align-self: center; - grid-auto-flow: column; - grid-gap: 18px; - } - - .bubble-icon { - display: flex; - height: 24px; - width: 24px; - color: var(--primary-text-color); - } - - .bubble-button { - display: flex; - background: var(--background-color-2, var(--secondary-background-color)); - height: 42px; - border-radius: 32px; - align-items: center; - justify-content: center; - cursor: pointer; - border: none; - } - - .large .bubble-cover-card-container { - height: 64px; - display: flex; - background: var(--background-color-2, var(--secondary-background-color)); - border-radius: 32px; - } - - .large .bubble-header-container { - height: 64px; - } - - .large .bubble-header { - width: 100%; - } - - .large .bubble-icon-container { - --mdc-icon-size: 28px; - min-width: 48px !important; - min-height: 48px !important; - margin-left: 2px; - align-content: center; - } - - .large .bubble-icon { - align-items: center; - } - - .large .bubble-buttons { - display: flex; - position: relative; - right: 18px; - align-self: center; - grid-gap: 18px; - } - - .large .bubble-sub-button-container { - margin-right: 14px; - } - - .rows-2 .bubble-sub-button-container { - flex-direction: column; - gap: 4px !important; - display: grid !important; - grid-template-columns: repeat(2, min-content); - grid-template-rows: repeat(2, 1fr); - grid-auto-flow: column; - width: auto; - padding-right: 14px; - } - - .rows-2 .bubble-sub-button { - height: 20px !important; - } -` \ No newline at end of file diff --git a/src/cards/empty-column/create.ts b/src/cards/empty-column/create.ts deleted file mode 100644 index 16d9b932..00000000 --- a/src/cards/empty-column/create.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createElement } from "../../tools/utils.ts"; -import styles from "./styles.ts"; - -export function createStructure(context) { - context.elements = {}; - context.elements.emptyColumnCard = createElement('div', 'bubble-empty-column empty-column'); - - context.elements.style = createElement('style'); - context.elements.style.innerText = styles; - context.elements.customStyle = createElement('style'); - - context.content.innerHTML = ''; - context.content.appendChild(context.elements.emptyColumnCard); - context.content.appendChild(context.elements.style); - context.content.appendChild(context.elements.customStyle); - - context.cardType = "empty-column"; -} diff --git a/src/cards/empty-column/index.ts b/src/cards/empty-column/index.ts deleted file mode 100644 index 46be1328..00000000 --- a/src/cards/empty-column/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createStructure } from './create.ts'; - -export function handleEmptyColumn(context) { - if (context.cardType !== "empty-column") { - createStructure(context); - } -} diff --git a/src/cards/empty-column/styles.ts b/src/cards/empty-column/styles.ts deleted file mode 100644 index 08bc488e..00000000 --- a/src/cards/empty-column/styles.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default ` - .empty-column { - display: flex; - width: 100%; - } -`; \ No newline at end of file diff --git a/src/cards/horizontal-buttons-stack/changes.ts b/src/cards/horizontal-buttons-stack/changes.ts deleted file mode 100644 index 8ef71de1..00000000 --- a/src/cards/horizontal-buttons-stack/changes.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { isColorCloseToWhite } from "../../tools/style.ts"; -import { getState } from "../../tools/utils.ts"; -import { createButton } from './create.ts'; -import { initializesubButtonIcon } from '../../tools/global-changes.ts'; - -const BUTTON_MARGIN = 12; - -export function sortButtons(context) { - if (!context.config.auto_order) return; - - const states = context._hass.states; - - context.elements.buttons.sort((a, b) => { - if (!states[a.pirSensor]) return 1; - if (!states[a.pirSensor]) return -1; - - const aTime = states[a.pirSensor]?.last_updated; - const bTime = states[b.pirSensor]?.last_updated; - - if (states[a.pirSensor]?.state === "on" && states[b.pirSensor]?.state === "on") { - return aTime > bTime ? -1 : aTime === bTime ? 0 : 1; - } - - // If only a.pirSensor is "on", place a before b - if (states[a.pirSensor]?.state === "on") return -1; - - // If only b.pirSensor is "on", place b before a - if (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 - return aTime > bTime ? -1 : aTime === bTime ? 0 : 1; - }); -} -export function placeButtons(context) { - let position = 0; - for (let i = 0; i < context.elements.buttons.length; ++i) { - let buttonWidth = localStorage.getItem(`bubbleButtonWidth-${context.elements.buttons[i].link}`); - - context.elements.buttons[i].style.width = ''; - const newWidth = context.elements.buttons[i].offsetWidth; - context.elements.buttons[i].style.width = `${newWidth}px`; - - if (newWidth > 0) { - buttonWidth = newWidth; - localStorage.setItem(`bubbleButtonWidth-${context.elements.buttons[i].link}`, `${newWidth}`); - } - - if (buttonWidth !== null) { - context.elements.buttons[i].style.transform = `translateX(${position}px)`; - context.elements.buttons[i].style.width = ''; - position += +buttonWidth + BUTTON_MARGIN; - } - } - context.elements.cardContainer.style.width = `${position - BUTTON_MARGIN}px`; -} -export function changeEditor(context) { - const detectedEditor = context.shadowRoot.host.closest('hui-card-preview, hui-card-options'); - - if (context.editor || detectedEditor !== null) { - context.elements.cardContainer.classList.add('editor'); - context.card.classList.add('editor'); - } else { - context.elements.cardContainer.classList.remove('editor'); - context.card.classList.remove('editor'); - } -} -export function changeLight(context) { - context.elements.buttons.forEach((button) => { - const entityData = context._hass.states[button.lightEntity]; - const rgbColor = entityData?.attributes.rgb_color; - const state = entityData?.state; - - if (rgbColor) { - const rgbColorOpacity = (isColorCloseToWhite(rgbColor) ? 'rgba(255, 220, 200, 0.5)' : `rgba(${rgbColor}, 0.5)`); - button.backgroundColor.style.backgroundColor = rgbColorOpacity; - button.backgroundColor.style.borderColor = 'rgba(0, 0, 0, 0)'; - } else if (state == 'on') { - button.backgroundColor.style.backgroundColor = 'rgba(255, 255, 255, 0.5)'; - button.backgroundColor.style.borderColor = 'rgba(0, 0, 0, 0)'; - } else { - button.backgroundColor.style.backgroundColor = 'rgba(0, 0, 0, 0)'; - button.backgroundColor.style.borderColor = 'var(--primary-text-color)'; - } - }); -} -export function changeConfig(context) { - context.elements.buttons.forEach((button) => { - const index = button.index; - const name = context.config[`${index}_name`] ?? ''; - const icon = context.config[`${index}_icon`] ?? ''; - const sensor = context.config[`${index}_pir_sensor`]; - const link = context.config[`${index}_link`]; - const entity = context.config[`${index}_entity`]; - - button.pirSensor = sensor; - button.lightEntity = entity; - button.link = link; - - if (name) { - button.name.innerText = name; - button.name.style.display = ''; - } else { - button.name.style.display = 'none'; - } - if (icon) { - button.icon.icon = icon; - button.icon.style.display = ''; - } else { - button.icon.style.display = 'none'; - } - - if (link === undefined) { - button.remove(); - context.elements.buttons = context.elements.buttons.filter((btn) => btn !== button); - context.elements.buttons.forEach((btn, idx) => { - btn.index = idx + 1; - }); - } - }); - - // Create a new button if necessary - let index = context.elements.buttons.length + 1; - while (context.config[`${index}_link`] !== undefined) { - const existingButton = context.elements.buttons.find(button => button.index === index); - if (!existingButton) { - const newButton = createButton(context, index); - context.elements.buttons.push(newButton); - } - index++; - } -} -export function changeStatus(context) { - if (context.content.scrollWidth > context.content.offsetWidth) { - context.content.classList.add('is-scrollable'); - } else { - context.content.classList.remove('is-scrollable'); - } -} -export function changeStyle(context) { - const state = getState(context); - - const customStyle = context.config.styles - ? Function('hass', 'entityId', 'state', 'return `' + context.config.styles + '`;')(context._hass, context.config.entity, state) - : ''; - - context.elements.customStyle.innerText = customStyle; -} \ No newline at end of file diff --git a/src/cards/horizontal-buttons-stack/create.ts b/src/cards/horizontal-buttons-stack/create.ts deleted file mode 100644 index 031f1bf8..00000000 --- a/src/cards/horizontal-buttons-stack/create.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { createElement, forwardHaptic } from "../../tools/utils.ts"; -import { addHash, removeHash } from "../pop-up/helpers.ts"; -import styles from "./styles.ts"; - -let isOpen = false; - -export function createButton(context, index) { - const name = context.config[`${index}_name`] ?? ''; - const icon = context.config[`${index}_icon`] ?? ''; - const sensor = context.config[`${index}_pir_sensor`]; - const link = context.config[`${index}_link`]; - const entity = context.config[`${index}_entity`]; - isOpen = isOpen || location.hash === link; - - const iconElement = createElement('ha-icon', 'bubble-icon icon'); - iconElement.icon = icon; - const nameElement = createElement('div', 'bubble-name name'); - nameElement.innerText = name; - const backgroundColorElement = createElement('div', 'bubble-background-color background-color'); - const backgroundElement = createElement('div', 'bubble-background background'); - const button = createElement('div', `bubble-button bubble-button-${index} button ${link.substring(1)}`); - let buttonWidth = localStorage.getItem(`bubbleButtonWidth-${link}`); - button.style.width = `${buttonWidth}px`; - - button.appendChild(iconElement); - button.appendChild(nameElement); - button.appendChild(backgroundColorElement); - button.appendChild(backgroundElement); - button.addEventListener('click', () => { - if (location.hash !== link) { - isOpen = false; - } - - if (isOpen) { - removeHash() - } else { - addHash(link); - } - isOpen = !isOpen; - - forwardHaptic("light"); - }); - - button.icon = iconElement; - button.name = nameElement; - button.backgroundColor = backgroundColorElement; - button.background = backgroundElement; - button.pirSensor = sensor; - button.lightEntity = entity; - button.link = link; - button.index = index; - - function handleUrlChange() { - if (!context.config.highlight_current_view) return; - - const isShown = location.pathname === link || location.hash === link; - if (isShown) { - button.classList.add("highlight"); - } else { - button.classList.remove("highlight"); - } - } - - window.addEventListener('urlChanged', handleUrlChange); - - context.elements.buttons.push(button); - - return button; -} - -export function createStructure(context) { - context.elements = {}; - context.elements.buttons = []; - context.elements.cardContainer = createElement('div', 'bubble-horizontal-buttons-stack-card-container horizontal-buttons-stack-container'); - - let index = 1; - while (context.config[index + '_link']) { - context.elements.cardContainer.appendChild(createButton(context, index)) - index++; - } - - context.elements.style = createElement('style'); - context.elements.style.innerText = styles; - context.elements.customStyle = createElement('style'); - - context.card.classList.add('horizontal-buttons-stack-card'); - context.card.style.marginLeft = context.config.margin ?? ''; - if (!context.config.hide_gradient) { - context.card.classList.add('has-gradient'); - } - context.card.style.setProperty('--desktop-width', context.config.width_desktop ?? '500px'); - context.elements.cardContainer.appendChild(context.elements.style); - context.elements.cardContainer.appendChild(context.elements.customStyle); - context.content.appendChild(context.elements.cardContainer); - - context.content.addEventListener('scroll', () => { - if (context.content.scrollLeft > 0) { - context.content.classList.add('is-scrolled'); - } else { - context.content.classList.remove('is-scrolled'); - } - - if (context.content.scrollWidth === context.content.offsetWidth + context.content.scrollLeft) { - context.content.classList.add('is-maxed-scroll'); - } else { - context.content.classList.remove('is-maxed-scroll'); - } - }); - - const riseAnimation = context.config.rise_animation ?? true; - - if (riseAnimation) { - context.content.style.animation = 'from-bottom .6s forwards'; - setTimeout(() => { - context.content.style.animation = 'none'; - }, 1500); - } - - // Fix for the last cards that are hidden by the HBS - let parentElement = context.card.parentNode.host; - if (parentElement && !context.editor) { - parentElement.style.padding = '0 0 80px'; - } - - context.cardType = "horizontal-buttons-stack"; -} \ No newline at end of file diff --git a/src/cards/horizontal-buttons-stack/index.ts b/src/cards/horizontal-buttons-stack/index.ts deleted file mode 100644 index 6138162f..00000000 --- a/src/cards/horizontal-buttons-stack/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { changeConfig, changeEditor, changeLight, changeStatus, changeStyle, placeButtons, sortButtons } from './changes.ts'; -import { createStructure } from './create.ts'; -import { configChanged } from "../../tools/utils.ts"; - -export function handleHorizontalButtonsStack(context) { - if (context.cardType !== "horizontal-buttons-stack") { - createStructure(context); - } - - changeStyle(context); - sortButtons(context); - changeConfig(context); - changeEditor(context); - placeButtons(context); - changeLight(context); - changeStatus(context); -} \ No newline at end of file diff --git a/src/cards/horizontal-buttons-stack/styles.ts b/src/cards/horizontal-buttons-stack/styles.ts deleted file mode 100644 index 34ab18f1..00000000 --- a/src/cards/horizontal-buttons-stack/styles.ts +++ /dev/null @@ -1,163 +0,0 @@ -export default ` - @keyframes from-bottom { - 0% { transform: translate(-50%, 100px); } - 26% { transform: translate(-50%, -8px); } - 46% { transform: translate(-50%, 1px); } - 62% { transform: translate(-50%, -2px); } - 70% { transform: translate(-50%, 0); } - 100% { transform: translate(-50%, 0); } - } - @keyframes pulse { - 0% { filter: brightness(0.7); } - 100% { filter: brightness(1.3); } - } - ha-card { - border-radius: 0; - } - .horizontal-buttons-stack-card { - bottom: 16px; - height: 51px; - margin-top: 0; - position: fixed; - width: calc(100% - var(--mdc-drawer-width, 0px) - 8px); - left: calc(var(--mdc-drawer-width, 0px) + 4px); - z-index: 6; /* Higher value hide the more-info panel */ - } - @media only screen and (max-width: 870px) { - .horizontal-buttons-stack-card { - width: calc(100% - 16px); - left: 8px; - } - - .horizontal-buttons-stack-card::before { - left: -10px; - } - } - .horizontal-buttons-stack-card::before { - content: ''; - position: absolute; - top: -32px; - display: none; - background: linear-gradient(0deg, var(--background-color, var(--primary-background-color)) 50%, rgba(79, 69, 87, 0)); - width: 200%; - height: 100px; - pointer-events: none; - } - .has-gradient.horizontal-buttons-stack-card::before { - display: block; - } - - .card-content { - width: calc(100% + 36px); - padding: 0 !important; - max-width: calc(var(--desktop-width) - 8px); - box-sizing: border-box; - overflow: scroll; - position: absolute; - left: 50%; - transform: translateX(-50%); - -ms-overflow-style: none; - scrollbar-width: none; - -webkit-mask-image: linear-gradient( - 90deg, - #000000 0%, - #000000 calc(0% + 28px), - #000000 calc(100% - 28px), - transparent 100% - ); - } - .is-scrollable.card-content { - padding: 0 !important; - width: 100%; - } - .is-scrolled.card-content { - padding: 0 !important; - width: 100%; - -webkit-mask-image: linear-gradient( - 90deg, - transparent 0%, - #000000 calc(0% + 28px), - #000000 calc(100% - 28px), - transparent 100% - ); - } - .is-maxed-scroll.card-content { - -webkit-mask-image: linear-gradient( - 90deg, - transparent 0%, - #000000 calc(0% + 28px), - #000000 calc(100% - 28px), - #000000 100% - ); - } - .card-content::-webkit-scrollbar { - display: none; - } - - .bubble-horizontal-buttons-stack-card-container { - height: 51px; - position: relative; - margin: auto; - } - - .bubble-button { - align-items: center; - border-radius: 25px; - color: var(--primary-text-color); - cursor: pointer; - display: inline-flex; - height: 50px; - left: 0; - padding: 0 16px; - position: absolute; - white-space: nowrap; - z-index: 1; - transition: transform 1s; - box-sizing: border-box; - } - .bubble-button.highlight { - animation: pulse 1.4s infinite alternate; - } - .bubble-background-color { - border: 1px solid var(--primary-text-color); - border-radius: 24px; - box-sizing: border-box; - height: 100%; - left: 0; - position: absolute; - top: 0; - transition: background-color 1s; - width: 100%; - z-index: -1; - } - .bubble-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)); - } - .bubble-icon { - height: 24px; - width: 24px; - } - .bubble-icon + .bubble-name { - margin-left: 8px; - } - - - .horizontal-buttons-stack-card.editor { - position: relative; - width: 100%; - left: 0; - bottom: 0; - } - .horizontal-buttons-stack-card.editor::before { - background: none; - } - -`; \ No newline at end of file diff --git a/src/cards/media-player/changes.ts b/src/cards/media-player/changes.ts deleted file mode 100644 index e8853e5c..00000000 --- a/src/cards/media-player/changes.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { initializesubButtonIcon } from '../../tools/global-changes.ts'; -import { - applyScrollingEffect, - getBrightness, - getIcon, - getIconColor, - getImage, - getName, - getState, - getAttribute, - isEntityType, - isStateOn, - getWeatherIcon, - setLayout -} from '../../tools/utils.ts'; - -export function changeIcon(context) { - const isOn = isStateOn(context); - const icon = getIcon(context); - const image = getImage(context); - - if (image !== '') { - context.elements.image.style.backgroundImage = 'url(' + image + ')'; - context.elements.icon.style.display = 'none'; - context.elements.image.style.display = ''; - } else if (icon !== '') { - context.elements.icon.icon = icon; - context.elements.icon.style.color = isOn ? 'var(--accent-color)' : 'inherit'; - context.elements.icon.style.display = ''; - context.elements.image.style.display = 'none'; - } else { - context.elements.icon.style.display = 'none'; - context.elements.image.style.display = 'none'; - } -} - -export function changeName(context) { - const name = getName(context); - - if (name !== context.previousName) { - context.elements.name.innerText = name; - context.previousName = name; - applyScrollingEffect(context, context.elements.name, name); - } -} - -export function changeMediaInfo(context) { - const title = getAttribute(context, "media_title"); - const artist = getAttribute(context, "media_artist"); - const state = title + artist; - - if (state !== context.previousState) { - if (artist === '') { - context.elements.artist.style.display = 'none'; - } else { - context.elements.artist.style.display = 'flex'; - } - - applyScrollingEffect(context, context.elements.title, title); - applyScrollingEffect(context, context.elements.artist, artist); - context.previousState = state; - } -} - -export function changeDisplayedInfo(context) { - const title = getAttribute(context, "media_title"); - const artist = getAttribute(context, "media_artist"); - const noMediaInfo = (title, artist) === ''; - - context.elements.mediaInfoContainer.style.display = noMediaInfo ? 'none' : ''; - context.elements.nameContainer.style.display = noMediaInfo ? '' : 'none'; -} - -export function changeSlider(context) { - if (isEntityType(context, "media_player") && context.dragging === false && context.elements.rangeFill) { - const percentage = 100 * getAttribute(context, "volume_level"); - context.elements.rangeFill.style.transform =`translateX(${percentage}%)`; - } -} - -export function changeStatus(context) { - const state = getState(context); - - if (state === 'unavailable') { - context.card.classList.add('is-unavailable'); - } else { - context.card.classList.remove('is-unavailable'); - } - - if (isStateOn(context)) { - context.card.classList.add('is-on'); - } else { - context.card.classList.remove('is-on'); - } -} - -export function changePlayPauseIcon(context) { - const isPlaying = getState(context) === 'playing'; - const clicked = context.elements.playPauseButton.clicked; - - if (isPlaying) { - context.elements.playPauseButton.setAttribute("icon", clicked ? "mdi:play" : "mdi:pause"); - } else { - context.elements.playPauseButton.setAttribute("icon", clicked ? "mdi:pause" : "mdi:play"); - } - - context.elements.playPauseButton.clicked = false; -} - -export function changePowerIcon(context) { - const isOn = isStateOn(context); - - if (!isOn) { - context.elements.powerButton.style.color = ""; - } else { - context.elements.powerButton.style.color = "var(--accent-color)"; - } -} - -export function changeVolumeIcon(context) { - if (context.elements.volumeButton.isHidden) { - context.elements.volumeButton.setAttribute("icon", "mdi:volume-high"); - context.elements.volumeButton.isHidden = false; - } else { - context.elements.volumeButton.setAttribute("icon", "mdi:close"); - context.elements.volumeButton.isHidden = true; - } -} - -export function changeMuteIcon(context) { - const isVolumeMuted = getAttribute(context, "is_volume_muted") === true; - const clicked = context.elements.muteButton.clicked; - - if (isVolumeMuted) { - context.elements.muteButton.style.color = clicked ? "" : "var(--accent-color)"; - } else { - context.elements.muteButton.style.color = clicked ? "var(--accent-color)" : ""; - } - - context.elements.muteButton.clicked = false; -} - -export function changeStyle(context) { - initializesubButtonIcon(context); - setLayout(context); - - const state = getState(context); - - const customStyle = context.config.styles - ? Function('hass', 'entityId', 'state', 'icon', 'subButtonIcon', 'getWeatherIcon', `return \`${context.config.styles}\`;`) - (context._hass, context.config.entity, state, context.elements.icon.icon, context.subButtonIcon, getWeatherIcon) - : ''; - - context.elements.customStyle.innerText = customStyle; -} \ No newline at end of file diff --git a/src/cards/media-player/create.ts b/src/cards/media-player/create.ts deleted file mode 100644 index 58bb749a..00000000 --- a/src/cards/media-player/create.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { addActions, addFeedback } from "../../tools/tap-actions.ts"; -import { createElement, toggleEntity, getAttribute, isStateOn } from "../../tools/utils.ts"; -import { onSliderChange } from "./helpers.ts"; -import { changeVolumeIcon } from "./changes.ts"; -import styles from "./styles.ts"; - -export function createStructure(context) { - context.dragging = false; - - context.elements = {}; - context.elements.mediaPlayerContainer = createElement('div', 'bubble-media-player-container'); - context.elements.mediaPlayerCard = createElement('div', 'bubble-media-player'); - context.elements.mediaInfoContainer = createElement('div', 'bubble-media-info-container'); - context.elements.nameContainer = createElement('div', 'bubble-name-container'); - context.elements.buttonContainer = createElement('div', 'bubble-button-container'); - context.elements.iconContainer = createElement('div', 'bubble-icon-container'); - context.elements.playPauseButton = createElement('ha-icon', 'bubble-play-pause-button'); - context.elements.previousButton = createElement('ha-icon', 'bubble-previous-button'); - context.elements.previousButton.setAttribute("icon", "mdi:skip-previous"); - context.elements.nextButton = createElement('ha-icon', 'bubble-next-button'); - context.elements.nextButton.setAttribute("icon", "mdi:skip-next"); - context.elements.volumeButton = createElement('ha-icon', 'bubble-volume-button'); - context.elements.volumeButton.setAttribute("icon", "mdi:volume-high"); - context.elements.powerButton = createElement('ha-icon', 'bubble-power-button'); - context.elements.powerButton.setAttribute("icon", "mdi:power-standby"); - context.elements.muteButton = createElement('ha-icon', 'bubble-mute-button is-hidden'); - context.elements.muteButton.setAttribute("icon", "mdi:volume-off"); - context.elements.title = createElement('div', 'bubble-title'); - context.elements.artist = createElement('div', 'bubble-artist'); - context.elements.name = createElement('div', 'bubble-name'); - context.elements.state = createElement('div', 'bubble-state'); - context.elements.icon = createElement('ha-icon', 'bubble-icon'); - context.elements.image = createElement('div', 'bubble-entity-picture'); - context.elements.style = createElement('style'); - context.elements.customStyle = createElement('style'); - - context.elements.style.innerText = styles; - - context.elements.iconContainer.appendChild(context.elements.icon); - context.elements.iconContainer.appendChild(context.elements.image); - context.elements.iconContainer.appendChild(context.elements.muteButton); - - context.elements.nameContainer.appendChild(context.elements.name); - context.elements.nameContainer.appendChild(context.elements.state); - - context.elements.mediaInfoContainer.appendChild(context.elements.title); - context.elements.mediaInfoContainer.appendChild(context.elements.artist); - - if (!context.config.hide?.power_button) context.elements.buttonContainer.appendChild(context.elements.powerButton); - if (!context.config.hide?.previous_button) context.elements.buttonContainer.appendChild(context.elements.previousButton); - if (!context.config.hide?.next_button) context.elements.buttonContainer.appendChild(context.elements.nextButton); - if (!context.config.hide?.volume_button) context.elements.buttonContainer.appendChild(context.elements.volumeButton); - if (!context.config.hide?.play_pause_button) context.elements.buttonContainer.appendChild(context.elements.playPauseButton); - - context.elements.mediaPlayerCard.appendChild(context.elements.iconContainer); - context.elements.mediaPlayerCard.appendChild(context.elements.mediaInfoContainer); - context.elements.mediaPlayerCard.appendChild(context.elements.nameContainer); - context.elements.mediaPlayerCard.appendChild(context.elements.buttonContainer); - - context.content.innerHTML = ''; - - context.content.appendChild(context.elements.mediaPlayerContainer); - context.content.appendChild(context.elements.style); - context.content.appendChild(context.elements.customStyle); - - context.elements.mediaPlayerContainer.appendChild(context.elements.mediaPlayerCard); - - addActions(context.elements.icon, context.config); - addActions(context.elements.image, context.config); - - // Volume slider - - context.elements.volumeSliderContainer = createElement('div', 'bubble-volume-slider is-hidden'); - createSlider(context, context.elements.volumeSliderContainer); - context.elements.mediaPlayerCard.appendChild(context.elements.volumeSliderContainer); - - context.elements.volumeButton.addEventListener('click', () => { - context.elements.volumeSliderContainer.classList.toggle('is-hidden'); - context.elements.muteButton.classList.toggle('is-hidden'); - context.elements.icon.classList.toggle('is-hidden'); - context.elements.image.classList.toggle('is-hidden'); - changeVolumeIcon(context); - }); - - // Power button event - - context.elements.powerButton.addEventListener('click', () => { - const isOn = isStateOn(context); - - context._hass.callService('media_player', isOn ? 'turn_off' : 'turn_on', { - entity_id: context.config.entity - }); - }); - - // Mute button event - - context.elements.muteButton.addEventListener('click', () => { - const isVolumeMuted = getAttribute(context, "is_volume_muted") === true; - - context._hass.callService('media_player', 'volume_mute', { - entity_id: context.config.entity, - is_volume_muted: isVolumeMuted ? false : true - }); - - context.elements.muteButton.clicked = true; - }); - - // Previous button event - - context.elements.previousButton.addEventListener('click', () => { - context._hass.callService('media_player', 'media_previous_track', { - entity_id: context.config.entity - }); - }); - - // Next button event - - context.elements.nextButton.addEventListener('click', () => { - context._hass.callService('media_player', 'media_next_track', { - entity_id: context.config.entity - }); - }); - - // Play button event - - context.elements.playPauseButton.addEventListener('click', () => { - context._hass.callService('media_player', 'media_play_pause', { - entity_id: context.config.entity - }); - - context.elements.playPauseButton.clicked = true; - }); - - context.cardType = `media-player`; -} - -function createSlider(context, sliderContainer) { - let initialX = 0; - let volumeLevel = Math.round(getAttribute(context, 'volume_level') * 100) + '%'; - - context.elements.rangeFill = createElement('div', 'bubble-range-fill range-fill'); - context.elements.rangeSlider = createElement('div', 'bubble-range-slider range-slider'); - context.elements.rangeValue = createElement('div', 'bubble-range-value'); - context.elements.rangeSlider.appendChild(context.elements.rangeValue); - context.elements.rangeSlider.appendChild(context.elements.rangeFill); - sliderContainer.appendChild(context.elements.rangeSlider); - - sliderContainer.addEventListener('pointercancel', onPointerCancel); - sliderContainer.addEventListener('pointerdown', (e) => { - sliderContainer.setPointerCapture(e.pointerId); - - if (context.card.classList.contains('is-unavailable')) { - return; - } - - context.dragging = true; - initialX = e.pageX || (e.touches ? e.touches[0].pageX : 0); - - sliderContainer.classList.add('is-dragging'); - sliderContainer.addEventListener('pointermove', onPointerMove); - sliderContainer.addEventListener('pointerup', onPointerUp); - }); - - function onPointerCancel() { - context.dragging = false; - - sliderContainer.classList.remove('is-dragging'); - sliderContainer.removeEventListener('pointermove', onPointerMove); - sliderContainer.removeEventListener('pointerup', onPointerUp); - } - - function onPointerMove(e) { - e.stopPropagation(); - - const moveX = e.pageX || (e.touches ? e.touches[0].pageX : 0); - if (Math.abs(initialX-moveX) > 10) { - onSliderChange(context, moveX, true); - } - } - - function onPointerUp(e) { - e.stopPropagation(); - - context.dragging = false; - - const moveX = e.pageX || (e.touches ? e.touches[0].pageX : 0); - onSliderChange(context, moveX); - - sliderContainer.classList.remove('is-dragging'); - sliderContainer.removeEventListener('pointermove', onPointerMove); - sliderContainer.removeEventListener('pointerup', onPointerUp); - } - - context.elements.rangeValue.innerText = volumeLevel; -} diff --git a/src/cards/media-player/helpers.ts b/src/cards/media-player/helpers.ts deleted file mode 100644 index 3750f136..00000000 --- a/src/cards/media-player/helpers.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - throttle, - getAttribute, - isEntityType -} from "../../tools/utils.ts"; - -export function updateEntity(context, value) { - if (isEntityType(context, "media_player")) { - context._hass.callService('media_player', 'volume_set', { - entity_id: context.config.entity, - volume_level: value / 100 - }); - } -}; - -export const throttledUpdateEntity = throttle(updateEntity); - -export function onSliderChange(context, leftDistance, throttle = false) { - const rect = context.elements.rangeSlider.getBoundingClientRect(); - const percentage = 100 * (leftDistance - rect.left) / rect.width; - const rangedPercentage = Math.round(Math.min(100, Math.max(0, percentage))); - - context.elements.rangeFill.style.transform =`translateX(${rangedPercentage}%)`; - if (throttle) { - throttledUpdateEntity(context, rangedPercentage); - } else { - updateEntity(context, rangedPercentage); - } - - context.elements.rangeValue.innerText = rangedPercentage + '%'; -} diff --git a/src/cards/media-player/index.ts b/src/cards/media-player/index.ts deleted file mode 100644 index e942edc0..00000000 --- a/src/cards/media-player/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { changeState, changeSubButtonState } from "../../tools/global-changes.ts"; -import { - changeStatus, - changeName, - changeMediaInfo, - changeDisplayedInfo, - changeIcon, - changeSlider, - changePlayPauseIcon, - changeMuteIcon, - changePowerIcon, - changeStyle -} from './changes.ts'; -import { - createSmallStructure, - createLargeStructure, - createStructure -} from './create.ts'; - -export function handleMediaPlayer(context) { - if (context.cardType !== `media-player`) { - createStructure(context); - } - - changeStatus(context); - changeName(context); - changeMediaInfo(context); - changeDisplayedInfo(context); - changeIcon(context); - changeState(context); - changeSlider(context); - changePlayPauseIcon(context); - changeMuteIcon(context); - changePowerIcon(context); - changeSubButtonState(context, context.content, context.elements.buttonContainer, true); - changeStyle(context); -} diff --git a/src/cards/media-player/styles.ts b/src/cards/media-player/styles.ts deleted file mode 100644 index 99f95a88..00000000 --- a/src/cards/media-player/styles.ts +++ /dev/null @@ -1,309 +0,0 @@ -export default ` - * { - -webkit-tap-highlight-color: transparent !important; - } - - ha-card { - margin-top: 0; - background: none; - opacity: 1; - } - .is-unavailable { - opacity: 0.5; - } - - .bubble-media-player-container { - position: relative; - width: 100%; - height: 50px; - background-color: var(--background-color-2,var(--secondary-background-color)); - border-radius: 25px; - mask-image: radial-gradient(white, black); - -webkit-transform: translateZ(0); - overflow: hidden; - touch-action: pan-y; - } - - .bubble-media-player { - display: flex; - position: absolute; - justify-content: space-between; - align-items: center; - height: 100%; - width: 100%; - transition: background-color 1.5s; - background-color: rgba(0,0,0,0); - } - - .bubble-button-container { - display: inline-grid; - grid-auto-flow: column; - gap: 10px; - align-self: center; - margin-right: 8px; - } - - .bubble-play-pause-button, - .bubble-previous-button, - .bubble-next-button, - .bubble-volume-button, - .bubble-power-button { - background: none; - border: none; - cursor: pointer; - border-radius: 100%; - padding: 6px; - height: 24px; - width: 24px; - transition: background 0.3s ease; - align-self: center; - } - - .bubble-play-pause-button { - background-color: var(--accent-color); - } - - .bubble-volume-slider { - position: absolute; - width: calc(100% - 150px); - height: 38px; - left: 50px; - overflow: hidden; - border-radius: 20px; - z-index: 1; - border: 2px solid var(--background-color-2, var(--secondary-background-color)); - background-color: var(--card-background-color, var(--ha-card-background)); - opacity: 1; - transition: opacity .2s, transform .2s; - transform: translateX(0); - } - - .bubble-range-value { - display: flex; - justify-content: flex-end; - height: 38px; - align-items: center; - padding-right: 14px; - font-size: 12px; - opacity: 0.8; - } - - .bubble-mute-button { - opacity: 1; - transition: opacity .2s, transform .2s; - transform: translateX(0); - } - - .is-hidden { - opacity: 0 !important; - pointer-events: none; - transform: translateX(14px); - } - - .bubble-range-fill { - z-index: -1; - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - left: -100%; - transition: all .3s; - background-color: var(--accent-color); - } - - .is-dragging .bubble-range-fill { - transition: none; - } - - .is-light .bubble-range-fill { - opacity: 0.5; - } - - .is-unavailable .bubble-button-card { - cursor: not-allowed; - } - - .bubble-range-slider { - cursor: ew-resize; - } - .is-unavailable .bubble-range-slider { - cursor: not-allowed; - } - - .bubble-icon-container { - display: flex; - flex-wrap: wrap; - width: 38px; - height: 38px; - min-width: 38px; - min-height: 38px; - align-items: center; - justify-content: center; - margin: 6px; - border-radius: 50%; - background-color: var(--card-background-color, var(--ha-card-background)); - overflow: hidden; - z-index: 1; - position: relative; - cursor: pointer; - } - - .bubble-icon { - opacity: 0.6; - } - - .is-on .bubble-icon { - filter: brightness(1.1); - opacity: 1; - } - - .bubble-icon, - .bubble-mute-button { - display: flex; - position: absolute; - height: 38px; - width: 38px; - justify-content: center; - align-items: center; - } - - .bubble-entity-picture { - background-size: cover; - background-position: center; - height: 100%; - width: 100%; - position: absolute; - } - - .bubble-media-info-container { - display: flex; - line-height: 14px; - font-size: 12px; - flex-direction: column; - justify-content: center; - flex-grow: 1; - margin-left: 4px; - pointer-events: none; - position: relative; - overflow: hidden; - } - - .bubble-title, - .bubble-name, - .bubble-state, - .bubble-artist { - display: flex; - margin: 2px 0; - position: relative; - white-space: nowrap; - } - - .bubble-title { - font-weight: 600; - } - - .bubble-name-container { - display: flex; - line-height: 1em; - flex-direction: column; - justify-content: center; - flex-grow: 1; - font-weight: 600; - margin-left: 4px; - pointer-events: none; - position: relative; - overflow: hidden; - } - - .bubble-name { - margin: 2px 0; - } - - .bubble-state { - font-size: 12px; - opacity: 0.7; - margin: 2px 0; - font-weight: normal; - } - - .bubble-sub-button-container { - right: 0 !important; - } - - @media screen and (max-width: 250px) { - .bubble-previous-button { - display: none; - } - } - - @media screen and (max-width: 206px) { - .bubble-next-button { - display: none; - } - } - - @media screen and (max-width: 160px) { - .bubble-volume-button { - display: none; - } - } - - @keyframes tap-feedback { - 0% {transform: translateX(-100%); opacity: 0;} - 64% {transform: translateX(0); opacity: 0.1;} - 100% {transform: translateX(100%); opacity: 0;} - } - - .large .bubble-media-player-container { - height: 64px; - border-radius: 34px; - } - - .large .bubble-icon-container { - --mdc-icon-size: 28px; - min-width: 48px !important; - min-height: 48px !important; - margin-left: 8px; - } - - .large .bubble-play-pause-button { - display: flex; - height: 48px; - width: 48px; - padding: 0; - align-items: center; - justify-content: center; - } - - .large .bubble-volume-slider { - height: 48px !important; - border-radius: 24px; - left: 66px !important; - width: calc(100% - 190px) !important; - } - - .large .bubble-range-value { - place-items: center; - height: 48px; - } - - .large .bubble-button-container { - align-items: center; - gap: 14px; - } - - .rows-2 .bubble-sub-button-container { - flex-direction: column; - gap: 4px !important; - display: grid !important; - grid-template-columns: repeat(2, 1fr); - grid-template-rows: repeat(2, minmax(auto, max-content)); - grid-auto-flow: column; - width: auto; - } - - .rows-2 .bubble-sub-button { - height: 20px !important; - } -`; \ No newline at end of file diff --git a/src/cards/pop-up/changes.ts b/src/cards/pop-up/changes.ts deleted file mode 100644 index 40b762d6..00000000 --- a/src/cards/pop-up/changes.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { isColorCloseToWhite } from "../../tools/style.ts"; -import { getIcon, getIconColor, getImage, getName, getState, isEntityType, isStateOn, getWeatherIcon } from "../../tools/utils.ts"; -import { getBackdrop } from "./create.ts"; -import { addHash, onEditorChange, removeHash } from "./helpers.ts"; -import { initializesubButtonIcon } from '../../tools/global-changes.ts'; - -export function changeEditor(context) { - const detectedEditor = context.verticalStack.host.closest('hui-card-preview'); - -if (context.sectionRow.classList.contains('card')) { - // Fix the empty space caused by the pop-ups in the section view - if (!context.editor && context.sectionRow.style.position !== 'absolute') { - context.sectionRow.style.position = 'absolute'; - } else if (context.editor && context.sectionRow.style.position !== '') { - context.sectionRow.style.position = ''; - } -} - - if (context.editor || detectedEditor !== null) { - context.popUp.classList.add('editor'); - - if (detectedEditor !== null) { - context.elements.popUpContainer.classList.remove('hidden'); - } else { - context.elements.popUpContainer.classList.add('hidden'); - } - } else { - context.popUp.classList.remove('editor'); - context.elements.popUpContainer.classList.remove('hidden'); - } - onEditorChange(context); -} - -export function changeStyle(context) { - initializesubButtonIcon(context); - - const state = getState(context); - const { backdropCustomStyle } = getBackdrop(context); - - const customStyle = context.config.styles - ? Function('hass', 'entityId', 'state', 'icon', 'subButtonIcon', 'getWeatherIcon', `return \`${context.config.styles}\`;`) - (context._hass, context.config.entity, state, context.elements.icon, context.subButtonIcon, getWeatherIcon) - : ''; - - if (context.elements.customStyle) { - context.elements.customStyle.innerText = customStyle; - } - backdropCustomStyle.innerText = customStyle; -} - -export function changeTriggered(context) { - let triggerEntity = context.config.trigger_entity ?? ''; - let triggerState = context.config.trigger_state ?? ''; - let triggerClose = context.config.trigger_close ?? false; - let triggerEntityState = context._hass.states[triggerEntity]?.state; - - if (!triggerEntity) return; - if (!triggerState) return; - if (context.oldTriggerEntityState === triggerEntityState) return; - - if (context.config.hash === location.hash) { - // Popup is opened: should we close it? - if (triggerClose && triggerState !== triggerEntityState) { - removeHash(); - } - } else { - // Popup is closed: should we open it? - if (triggerEntityState === triggerState) { - addHash(context.config.hash); - } - } - - context.oldTriggerEntityState = triggerEntityState; -} \ No newline at end of file diff --git a/src/cards/pop-up/create.ts b/src/cards/pop-up/create.ts deleted file mode 100644 index 4627395a..00000000 --- a/src/cards/pop-up/create.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { convertToRGBA } from "../../tools/style.ts"; -import { addActions } from "../../tools/tap-actions.ts"; -import { createElement, toggleEntity, configChanged } from "../../tools/utils.ts"; -import { onUrlChange, removeHash } from "./helpers.ts"; -import styles, { backdropStyles } from "./styles.ts"; - -let backdrop; -let hideBackdrop = false; -let startTouchY; -let lastTouchY; - -export function getBackdrop(context) { - if (backdrop) { - return backdrop; - } - - const themeColorBackground = - getComputedStyle(document.body).getPropertyValue('--ha-card-background') || - getComputedStyle(document.body).getPropertyValue('--card-background-color'); - - const backdropStyle = createElement('style'); - backdropStyle.innerHTML = ` - ${backdropStyles} - .bubble-backdrop { - background-color: ${convertToRGBA(themeColorBackground, 0.7, 0.7)}; - } - `; - document.head.appendChild(backdropStyle); - - const backdropCustomStyle = createElement('style'); - document.head.appendChild(backdropCustomStyle); - - const backdropElement = createElement('div', 'bubble-backdrop backdrop is-hidden'); - if (context.config.hide_backdrop) { - backdropElement.style.display = 'none'; - backdropElement.style.pointerEvents = 'none'; - } - document.body.appendChild(backdropElement); - - function showBackdrop() { - backdropElement.classList.add('is-visible'); - backdropElement.classList.remove('is-hidden'); - } - - function hideBackdrop() { - backdropElement.classList.add('is-hidden'); - backdropElement.classList.remove('is-visible'); - } - - backdrop = { hideBackdrop, showBackdrop, backdropElement, backdropCustomStyle }; - - return backdrop; -} - -export function createHeader(context) { - context.elements = {}; - context.elements.closeIcon = createElement('ha-icon', 'bubble-close-icon'); - context.elements.closeIcon.icon = 'mdi:close'; - context.elements.closeButton = createElement("button", "bubble-close-button close-pop-up"); - context.elements.closeButton.addEventListener('click', removeHash); - context.elements.closeButton.appendChild(context.elements.closeIcon); - - context.elements.buttonContainer = createElement('div', 'bubble-button-container'); - context.elements.header = createElement('div', 'bubble-header'); - - const existingHeader = context.popUp.querySelector('.bubble-header-container'); - if (existingHeader === null) { - context.elements.headerContainer = createElement("div", 'bubble-header-container'); - context.elements.headerContainer.setAttribute("id", "header-container"); - context.elements.headerContainer.appendChild(context.elements.header); - context.elements.headerContainer.appendChild(context.elements.closeButton); - context.elements.header.appendChild(context.elements.buttonContainer); - } else { - context.elements.headerContainer = existingHeader; - context.elements.closeIcon = existingHeader.querySelector('.bubble-close-icon'); - context.elements.closeButton = existingHeader.querySelector('.bubble-close-button'); - context.elements.buttonContainer = existingHeader.querySelector('.bubble-button-container'); - context.elements.header = existingHeader.querySelector('.bubble-header'); - } - - context.popUp.addEventListener('touchstart', (event) => { - startTouchY = event.touches[0].clientY; - }, { passive: true }); - - context.elements.header.addEventListener('touchmove', (event) => { - const touchY = event.touches[0].clientY; - const offset = touchY - startTouchY; - if (offset > 0) { - context.popUp.style.transform = `translateY(${offset}px)`; - } - }, { passive: true }); - - context.elements.header.addEventListener('touchend', (event) => { - const touchY = event.changedTouches[0].clientY; - const offset = touchY - startTouchY; - if (offset > 50) { - removeHash(); - } else { - context.popUp.style.transform = ''; - } - }, { passive: true }); -} - -export function createStructure(context) { - try { - context.elements.style = createElement('style'); - context.elements.customStyle = createElement('style'); - - context.content.appendChild(context.elements.style); - context.content.appendChild(context.elements.customStyle); - - const themeColorBackground = - getComputedStyle(document.body).getPropertyValue('--ha-card-background') || - getComputedStyle(document.body).getPropertyValue('--card-background-color'); - - const color = context.config.bg_color ? context.config.bg_color : themeColorBackground; - const opacity = context.config.bg_opacity ?? 88; - const rgbaColor = convertToRGBA(color, (opacity / 100), 1.02); - context.popUp.style.backgroundColor = rgbaColor; - context.popUp.style.setProperty('--desktop-width', context.config.width_desktop ?? '540px'); - - if (context.config.close_on_click) { - context.popUp.addEventListener('touchend', removeHash); - } - - const contextOnUrlChange = onUrlChange(context); - - setTimeout(() => { - contextOnUrlChange(); - }, 0); - - window.addEventListener('location-changed', contextOnUrlChange); - window.addEventListener('popstate', contextOnUrlChange); - window.addEventListener('keydown', (event) => { - if (event.key === 'Escape' && context.config.hash === location.hash) { - removeHash(); - } - }, { passive: true }); - - context.popUp.addEventListener('touchmove', (event) => { - // Calculate the distance the finger has traveled - let touchMoveDistance = event.touches[0].clientY - startTouchY; - - // If the distance is positive (i.e., the finger is moving downward) and exceeds a certain threshold, close the pop-up - if (touchMoveDistance > 300 && event.touches[0].clientY > lastTouchY) { - removeHash(); - } - - // Update the Y position of the last touch - lastTouchY = event.touches[0].clientY; - }, { passive: true }); - - const existingContainer = context.popUp.querySelector('.bubble-pop-up-container'); - if (existingContainer === null) { - - context.elements.popUpContainer = createElement('div'); - context.elements.popUpContainer.classList.add('bubble-pop-up-container'); - let child = context.popUp.firstChild; - - while (child) { - context.elements.popUpContainer.appendChild(child); - child = context.popUp.firstChild; - } - } else { - context.elements.popUpContainer = existingContainer; - } - - context.popUp.appendChild(context.elements.headerContainer); - context.popUp.appendChild(context.elements.popUpContainer); - - } catch (e) { - console.error(e) - } -} - -export function prepareStructure(context) { - try { - context.cardType = "pop-up"; - context.verticalStack = context.getRootNode(); - context.sectionRow = context.verticalStack.host.parentElement; - context.popUp = context.verticalStack.querySelector('#root'); - context.popUp.classList.add('bubble-pop-up', 'pop-up', 'is-popup-closed'); - context.verticalStack.removeChild(context.popUp); - context.elements = {}; - getBackdrop(context); - - hideBackdrop = hideBackdrop || (context.config.hide_backdrop ?? false); - - context.popUp.style.setProperty('--custom-height-offset-desktop', context.config.margin_top_desktop ?? '0px'); - context.popUp.style.setProperty('--custom-height-offset-mobile', context.config.margin_top_mobile ?? '0px'); - context.popUp.style.setProperty('--custom-margin', `-${context.config.margin ?? '7px'}`); - context.popUp.style.setProperty('--custom-backdrop-filter', hideBackdrop ? 'none' : `blur(${context.config.bg_blur ?? 10}px)`); - context.popUp.style.setProperty('--custom-popup-filter', hideBackdrop ? `blur(${context.config.bg_blur ?? 10}px)` : 'none'); - context.popUp.style.setProperty('--custom-shadow-opacity', (context.config.shadow_opacity ?? 0) / 100); - - - const style = createElement('style'); - context.elements.customStyle = createElement('style'); - style.innerText = styles; - context.popUp.appendChild(style); - context.popUp.appendChild(context.elements.customStyle); - - } catch (e) { - console.error(e) - } -} \ No newline at end of file diff --git a/src/cards/pop-up/helpers.ts b/src/cards/pop-up/helpers.ts deleted file mode 100644 index 050545e4..00000000 --- a/src/cards/pop-up/helpers.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { getBackdrop } from "./create.ts"; - -let popupCount = 0; - -export function clickOutside(event) { - const targets = event.composedPath(); - const popupTarget = targets.find((target) => { - return ( - (target.classList && target.classList.contains('bubble-pop-up')) || - target.nodeName === 'HA-MORE-INFO-DIALOG' || - target.nodeName === 'HA-DIALOG-DATE-PICKER' - ); - }); - - if (popupTarget === undefined) { - removeHash(); - } -} - -export function removeHash() { - const newURL = window.location.href.split('#')[0]; - - history.replaceState(null, "", newURL); - window.dispatchEvent(new Event('location-changed')); -} -export function addHash(hash) { - const newURL = hash.startsWith('#') ? window.location.href.split('#')[0] + hash : hash; - - history.pushState(null, "", newURL); - window.dispatchEvent(new Event('location-changed')); -} -export function closePopup(context) { - if (context.popUp.classList.contains('is-popup-opened') === false) { - return; - } - - popupCount--; - document.body.style.overflow = ''; - context.popUp.classList.add('is-popup-closed'); - context.popUp.classList.remove('is-popup-opened'); - context.hideContentTimeout = setTimeout(function() { - context.popUp.style.display = 'none'; - }, 380); - context.resetCloseTimeout = () => { - clearTimeout(context.closeTimeout); - } - context.popUp.removeEventListener('touchstart', context.resetCloseTimeout); - window.removeEventListener('click', clickOutside); - - const closeOnClick = context.config.close_on_click ?? false; - if (closeOnClick) { - context.popUp.removeEventListener('mouseup', removeHash); - context.popUp.removeEventListener('touchend', removeHash); - } - - context.removeDomTimeout = window.setTimeout(() => { - if (context.popUp.parentNode === context.verticalStack) { - context.verticalStack.removeChild(context.popUp); - } - }, 360); -} -export function openPopup(context) { - if (context.popUp.classList.contains('is-popup-opened')) { - return; - } - - context.popUp.classList.add('is-popup-opened'); - - window.clearTimeout(context.removeDomTimeout); - if (context.popUp.parentNode !== context.verticalStack) { - context.verticalStack.appendChild(context.popUp); - } - popupCount++; - clearTimeout(context.closeTimeout); - clearTimeout(context.hideContentTimeout); - document.body.style.overflow = 'hidden'; - context.popUp.style.display = ''; - context.popUp.style.transform = ''; - context.popUp.addEventListener('touchstart', context.resetCloseTimeout, { passive: true }); - - const closeOnClick = context.config.close_on_click ?? false; - if (closeOnClick) { - context.popUp.addEventListener('mouseup', removeHash, { passive: true }); - context.popUp.addEventListener('touchend', removeHash, { passive: true }); - } - - requestAnimationFrame(() => { - context.popUp.classList.remove('is-popup-closed'); - window.addEventListener('click', clickOutside, { passive: true }); - }); - - if (context.config.auto_close > 0) { - context.closeTimeout = setTimeout(removeHash, context.config.auto_close); - } -} -export function onUrlChange(context) { - const { hideBackdrop, showBackdrop } = getBackdrop(context); - - return function() { - if (context.config.hash === location.hash) { - openPopup(context); - } else { - closePopup(context); - } - - if (popupCount === 0 || context.editor) { - hideBackdrop(); - } else { - showBackdrop(); - } - } -} -export function onEditorChange(context) { - const { hideBackdrop, showBackdrop } = getBackdrop(context); - const detectedEditor = context.verticalStack.host.closest('hui-card-preview'); - - if (context.editor || detectedEditor !== null) { - hideBackdrop(); - window.clearTimeout(context.removeDomTimeout); - if (context.popUp.parentNode !== context.verticalStack) { - context.verticalStack.appendChild(context.popUp); - } - } else { - if (context.config.hash === location.hash) { - openPopup(context); - showBackdrop(); - } else if (context.popUp.parentNode === context.verticalStack) { - context.verticalStack.removeChild(context.popUp); - } - } -} \ No newline at end of file diff --git a/src/cards/pop-up/index.ts b/src/cards/pop-up/index.ts deleted file mode 100644 index e4e1f368..00000000 --- a/src/cards/pop-up/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { changeEditor, changeIcon, changeLight, changeName, changeState, changeStatus, changeStyle, changeTriggered } from './changes.ts'; -import { createHeader, createStructure, prepareStructure } from './create.ts'; -import { configChanged } from "../../tools/utils.ts"; -import { handleButton } from "../../cards/button/index.ts"; -import { getButtonType } from "../../cards/button/helpers.ts"; - -export async function handlePopUp(context) { - if (context.cardType !== "pop-up") { - if ((context.getRootNode() instanceof ShadowRoot) === false) { - // The card is not added in the DOM - return; - } - prepareStructure(context); - createHeader(context); - createStructure(context); - } - - if ( - context.cardType !== "pop-up" || - context.popUp.classList.contains('is-popup-opened') || - configChanged(context, context.popUp) - ){ - changeStyle(context); - - if (context.config.entity || context.config.name) { - handleButton(context, context.elements.buttonContainer, context.elements.header); - } - } - - changeTriggered(context); - changeEditor(context); -} diff --git a/src/cards/pop-up/styles.ts b/src/cards/pop-up/styles.ts deleted file mode 100644 index 791131c1..00000000 --- a/src/cards/pop-up/styles.ts +++ /dev/null @@ -1,170 +0,0 @@ -export default ` - .bubble-pop-up-container { - display: flex; - flex-direction: column; - overflow: scroll; - height: calc(100% + 50px); - margin-top: -50px; - max-width: 100%; - padding-top: 50px; - padding-bottom: 80px; - grid-gap: 14px !important; - gap: 14px !important; - column-gap: 14px !important; - --grid-gap: 14px; - --vertical-stack-card-gap: 14px; - --horizontal-stack-card-gap: 14px; - --stack-card-gap: 14px; - -ms-overflow-style: none; /* for Internet Explorer, Edge */ - scrollbar-width: none; /* for Firefox */ - overflow-y: auto; - overflow-x: hidden; - grid-auto-rows: min-content; - mask-image: linear-gradient(to bottom, transparent 0px, black 40px, black calc(100% - 40px), transparent 100%); - -webkit-mask-image: linear-gradient(to bottom, transparent 0px, black 40px, black calc(100% - 40px), transparent 100%); - } - .bubble-pop-up.card-content { - width: 100% !important; - padding: 0 !important; - } - #root { - display: flex !important; - gap: 0 !important; - } - .bubble-pop-up { - display: flex !important; - transition: transform .36s; - position: fixed; - width: 100%; - max-width: 100%; - border-radius: 42px 42px 0 0; - box-sizing: border-box; - margin-left: var(--custom-margin); - padding: 18px 18px calc(50px + var(--custom-height-offset-mobile)) 18px; - left: 8px; - z-index: 5 !important; - bottom: calc(-50px - var(--custom-height-offset-mobile)); - } - .bubble-pop-up-container::-webkit-scrollbar { - display: none; /* for Chrome, Safari, and Opera */ - } - .bubble-pop-up > :first-child { - position: sticky; - top: 0; - z-index: 1; - background: none !important; - overflow: visible; - } - .is-popup-opened { - transform: translateY(0); - box-shadow: 0px 0px 50px rgba(0, 0, 0, var(--custom-shadow-opacity)); - backdrop-filter: var(--custom-popup-filter); - -webkit-backdrop-filter: var(--custom-popup-filter); - } - .is-popup-closed { - transform: translateY(100%) !important; - box-shadow: none !important; - backdrop-filter: none !important; - -webkit-backdrop-filter: none !important; - } - @media only screen and (min-width: 600px) { - .pop-up { - margin-left: 0 !important; - bottom: calc(-50px - var(--custom-height-offset-desktop)) !important; - min-width: var(--desktop-width, 540px); - max-width: var(--desktop-width, 540px); - left: calc(50% - (var(--desktop-width) / 2)); - padding: 18px 18px calc(50px + var(--custom-height-offset-desktop)) 18px; - } - } - @media only screen and (min-width: 870px) { - .pop-up { - left: calc(var(--mdc-drawer-width, 0px) / 2 + 50% - (var(--desktop-width) / 2)); - } - } - .bubble-pop-up.editor { - position: inherit !important; - width: 100% !important; - padding: 18px !important; - backdrop-filter: none !important; - display: block !important; - transform: none !important; - height: auto !important; - min-width: auto; - border-radius: 42px; - } - .bubble-header-container { - display: inline-flex; - height: 50px; - width: 100%; - margin: 0; - padding: 0; - } - .bubble-range-fill { - opacity: .5; - } - .bubble-header { - display: inline-flex; - position: relative; - flex-grow: 1; - margin-right: 14px; - } - .bubble-name { - font-size: 16px; - font-weight: heavy; - } - .bubble-close-button { - height: 50px; - width: 50px; - border: none; - border-radius: 50%; - z-index: 1; - background: var(--background-color,var(--secondary-background-color)); - color: var(--primary-text-color); - flex-shrink: 0; - cursor: pointer; - } - .bubble-button-card-container { - background: var(--background-color,var(--secondary-background-color)) !important; - backdrop-filter: blur(14px); - -webkit-backdrop-filter: blur(14px); - } - .bubble-pop-up-container.hidden { - height: 140px !important; - mask-image: linear-gradient(to bottom, transparent 0px, black 40px, black calc(100% - 40px), transparent 100%) !important; - -webkit-mask-image: linear-gradient(to bottom, transparent 0px, black 40px, black calc(100% - 40px), transparent 100%) !important; - } - .bubble-pop-up.editor > .bubble-pop-up-container { - padding-bottom: 0 !important; - mask-image: none; - -webkit-mask-image: none; - } -`; - -export const backdropStyles = ` - .bubble-backdrop { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 4; - opacity: 0; - transition: all 0.3s; - transition-delay: .1s; - display: flex; - } - - .bubble-backdrop.is-visible { - opacity: 1; - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - } - - .bubble-backdrop.is-hidden { - opacity: 0; - backdrop-filter: none; - -webkit-backdrop-filter: none; - pointer-events: none; - } -` \ No newline at end of file diff --git a/src/cards/separator/changes.ts b/src/cards/separator/changes.ts deleted file mode 100644 index 086e2a6d..00000000 --- a/src/cards/separator/changes.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { getIcon, getName, getState, getWeatherIcon, setLayout } from "../../tools/utils.ts"; -import { initializesubButtonIcon } from '../../tools/global-changes.ts'; - -export function changeIcon(context) { - context.elements.icon.icon = getIcon(context); -} -export function changeName(context) { - const name = getName(context); - if (name !== context.elements.name.innerText) { - context.elements.name.innerText = name; - } -} -export function changeStyle(context) { - initializesubButtonIcon(context); - setLayout(context); - - const state = getState(context); - - const customStyle = context.config.styles - ? Function('hass', 'entityId', 'state', 'icon', 'subButtonIcon', 'getWeatherIcon', `return \`${context.config.styles}\`;`) - (context._hass, context.config.entity, state, context.elements.icon.icon, context.subButtonIcon, getWeatherIcon) - : ''; - - context.elements.customStyle.innerText = customStyle; -} \ No newline at end of file diff --git a/src/cards/separator/create.ts b/src/cards/separator/create.ts deleted file mode 100644 index 3baf26a7..00000000 --- a/src/cards/separator/create.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createElement } from "../../tools/utils.ts"; -import { addActions } from "../../tools/tap-actions.ts"; -import styles from "./styles.ts"; - -export function createStructure(context) { - context.elements = {}; - context.elements.separatorCard = createElement('div', 'bubble-separator separator-container'); - - context.elements.icon = createElement('ha-icon', 'bubble-icon'); - context.elements.name = createElement('h4', 'bubble-name'); - context.elements.line = createElement('div', 'bubble-line'); - - context.elements.style = createElement('style'); - context.elements.style.innerText = styles; - context.elements.customStyle = createElement('style'); - - context.elements.separatorCard.appendChild(context.elements.icon); - context.elements.separatorCard.appendChild(context.elements.name); - context.elements.separatorCard.appendChild(context.elements.line); - - context.content.innerHTML = ''; - context.content.appendChild(context.elements.separatorCard); - context.content.appendChild(context.elements.style); - context.content.appendChild(context.elements.customStyle); - - context.cardType = "separator"; -} \ No newline at end of file diff --git a/src/cards/separator/index.ts b/src/cards/separator/index.ts deleted file mode 100644 index 876cdf28..00000000 --- a/src/cards/separator/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { changeSubButtonState } from "../../tools/global-changes.ts"; -import { changeState } from "../../tools/global-changes.ts"; -import { - changeIcon, - changeName, - changeStyle -} from './changes.ts' -import { createStructure } from './create.ts'; - -export function handleSeparator(context) { - if (context.cardType !== "separator") { - createStructure(context); - } - - changeIcon(context); - changeName(context); - changeSubButtonState(context, context.content, context.elements.separatorCard); - changeStyle(context); -} diff --git a/src/cards/separator/styles.ts b/src/cards/separator/styles.ts deleted file mode 100644 index 66c05a72..00000000 --- a/src/cards/separator/styles.ts +++ /dev/null @@ -1,54 +0,0 @@ -export default ` - .bubble-separator { - display: flex; - width: 100%; - padding: 4px 0; - align-items: center; - } - .bubble-icon { - display: inline-flex; - height: 24px; - width: 24px; - margin: 0 22px 0 8px; - } - .bubble-name { - margin: 0 30px 0 0; - font-size: 16px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .bubble-name:empty { - display: none; - } - .bubble-line { - border-radius: 6px; - opacity: 0.5; - flex-grow: 1; - height: 6px; - background-color: var(--background-color, var(--secondary-background-color)); - } - .bubble-sub-button-container { - margin-left: 24px; - } - - .large .bubble-separator { - height: 58px; - } - - .rows-2 .bubble-sub-button-container { - flex-direction: column; - gap: 4px !important; - display: grid !important; - grid-template-columns: repeat(2, 1fr); - grid-template-rows: repeat(2, minmax(auto, max-content)); - grid-auto-rows: none; - grid-auto-flow: column; - width: auto; - padding-right: 14px; - } - - .rows-2 .bubble-sub-button { - height: 20px !important; - } -`; \ No newline at end of file diff --git a/src/editor/bubble-card-editor.ts b/src/editor/bubble-card-editor.ts deleted file mode 100644 index ae8fbb54..00000000 --- a/src/editor/bubble-card-editor.ts +++ /dev/null @@ -1,1790 +0,0 @@ -import { version } from '../var/version.ts'; -import { - fireEvent, - isStateOn, - getState, - getAttribute, - getIcon -} from '../tools/utils.ts'; - -const LitElement = Object.getPrototypeOf(customElements.get("ha-panel-lovelace")); -const html = LitElement?.prototype.html; -const css = LitElement?.prototype.css; - -export function createBubbleCardEditor() { - if (!LitElement) { - console.error("The Bubble Card editor needs LitElement to be defined"); - return; - } - - class BubbleCardEditor extends LitElement { - - setConfig(config) { - this._config = { - ...config - }; - } - - static get properties() { - return { - hass: {}, - _config: {} - }; - } - - get _card_type() { - return this._config.card_type || ''; - } - - get _button_type() { - return this._config.button_type || - (this._config.card_type === 'pop-up' ? '' : '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 || false; - } - - get _margin() { - return this._config.margin || '7px'; - } - - get _margin_top_mobile() { - return this._config.margin_top_mobile || '0px'; - } - - get _margin_top_desktop() { - return this._config.margin_top_desktop || '0px'; - } - - get _width_desktop() { - return this._config.width_desktop || '540px'; - } - - get _bg_color() { - return this._config.bg_color || ''; - } - - get _bg_opacity() { - return this._config.bg_opacity !== undefined ? this._config.bg_opacity : '88'; - } - - get _bg_blur() { - return this._config.bg_blur !== undefined ? this._config.bg_blur : '14'; - } - - get _shadow_opacity() { - return this._config.shadow_opacity !== undefined ? this._config.shadow_opacity : '0'; - } - - get _rise_animation() { - return this._config.rise_animation !== undefined ? this._config.rise_animation : true; - } - - get _auto_close() { - return this._config.auto_close || ''; - } - - get _close_on_click() { - return this._config.close_on_click || false; - } - - get _background_update() { - return this._config.background_update || false; - } - - get _icon_open() { - return this._config.icon_open || ''; - } - - get _icon_close() { - return this._config.icon_close || ''; - } - - get _icon_down() { - return this._config.icon_down || ''; - } - - get _icon_up() { - return this._config.icon_up || ''; - } - - get _open_service() { - return this._config.open_service || 'cover.open_cover'; - } - - 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 || false; - } - - get _highlight_current_view() { - return this._config.highlight_current_view || false; - } - - get _show_state() { - const defaultState = this._config.card_type === 'state' ? true : false; - return this._config.show_state || defaultState; - } - - get _show_attribute() { - const defaultState = this._config.card_type === 'state' ? true : false; - return this._config.show_attribute || defaultState; - } - - get _show_last_changed() { - const defaultState = this._config.card_type === 'state' ? true : false; - return this._config.show_last_changed || this._config.show_last_updated || defaultState; - } - - get _attribute() { - return this._config.attribute || false; - } - - get _hide_backdrop() { - return this._config.hide_backdrop || false; - } - - get _hide_gradient() { - return this._config.hide_gradient || false; - } - - get _hide_play_pause_button() { - return this._config.hide?.play_pause_button || false; - } - - get _hide_next_button() { - return this._config.hide?.next_button || false; - } - - get _hide_previous_button() { - return this._config.hide?.previous_button || false; - } - - get _hide_volume_button() { - return this._config.hide?.volume_button || false; - } - - get _hide_power_button() { - return this._config.hide?.power_button || false; - } - - get _sub_button() { - return this._config.sub_button || ''; - } - - get _button_action() { - return this._config.button_action || ''; - } - - get _tap_action() { - return { - action: this._config.tap_action?.action || "more-info", - navigation_path: this._config.tap_action?.navigation_path || "", - url_path: this._config.tap_action?.url_path || "", - service: this._config.tap_action?.service || "", - target_entity: this._config.tap_action?.target?.entity_id || "", - data: this._config.tap_action?.data || "" - }; - } - - get _double_tap_action() { - return { - action: this._config.double_tap_action?.action || "toggle", - navigation_path: this._config.double_tap_action?.navigation_path || "", - url_path: this._config.double_tap_action?.url_path || "", - service: this._config.double_tap_action?.service || "", - target_entity: this._config.double_tap_action?.target?.entity_id || "", - data: this._config.double_tap_action?.data || "" - }; - } - - get _hold_action() { - return { - action: this._config.hold_action?.action || "toggle", - navigation_path: this._config.hold_action?.navigation_path || "", - url_path: this._config.hold_action?.url_path || "", - service: this._config.hold_action?.service || "", - target_entity: this._config.hold_action?.target?.entity_id || "", - data: this._config.hold_action?.data || "" - }; - } - - render() { - if (!this.hass) { - return html``; - } - - const root = document.querySelector("body > home-assistant").shadowRoot; - const dialog = root.querySelector("hui-dialog-edit-card").shadowRoot; - const previewElement = dialog.querySelector("ha-dialog > div.content > div.element-preview"); - - // Change the default preview element to be sticky - if (previewElement.style.position !== 'sticky') { - previewElement.style.position = 'sticky'; - previewElement.style.top = '0'; - } - - if (!this.listsUpdated) { - const formateList = item => ({ - label: item, - value: item - }); - - this.allEntitiesList = Object.keys(this.hass.states).map(formateList); - - this.lightList = Object.keys(this.hass.states).filter( - (eid) => eid.substr(0, eid.indexOf(".")) === "light" - ).map(formateList); - - this.sensorList = Object.keys(this.hass.states).filter( - (eid) => eid.substr(0, eid.indexOf(".")) === "sensor" - ).map(formateList); - - this.binarySensorList = Object.keys(this.hass.states).filter( - (eid) => eid.substr(0, eid.indexOf(".")) === "binary_sensor" - ).map(formateList); - - this.coverList = Object.keys(this.hass.states).filter( - (eid) => eid.substr(0, eid.indexOf(".")) === "cover" - ).map(formateList); - - this.mediaPlayerList = Object.keys(this.hass.states).filter( - (eid) => eid.substr(0, eid.indexOf(".")) === "media_player" - ).map(formateList); - - this.attributeList = Object.keys(this.hass.states[this._entity]?.attributes || {}).map((attributeName) => { - let entity = this.hass.states[this._entity]; - let formattedName = this.hass.formatEntityAttributeName(entity, attributeName); - return { label: formattedName, value: attributeName }; - }); - - 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': 'Media player', - 'value': 'media-player' - }, - { - 'label': 'Pop-up', - 'value': 'pop-up' - }, - { - 'label': 'Separator', - 'value': 'separator' - } - ]; - - this.buttonTypeList = [{ - 'label': 'Switch', - 'value': 'switch' - }, - { - 'label': 'Slider', - 'value': 'slider' - }, - { - 'label': 'State', - 'value': 'state' - }, - { - 'label': 'Name / Text', - 'value': 'name' - } - ]; - - this.tapActionTypeList = [{ - 'label': 'More info', - 'value': 'more-info' - }, - { - 'label': 'Toggle', - 'value': 'toggle' - }, - { - 'label': 'Navigate', - 'value': 'navigate' - }, - { - 'label': 'URL', - 'value': 'url' - }, - { - 'label': 'Call service', - 'value': 'call-service' - }, - { - 'label': 'No action', - 'value': 'none' - } - ]; - - this.listsUpdated = true; - } - - const allEntitiesList = this.allEntitiesList; - const lightList = this.lightList; - const sensorList = this.sensorList; - const coverList = this.coverList; - const cardTypeList = this.cardTypeList; - const buttonTypeList = this.buttonTypeList; - const nameButton = this._config.button_type === 'name'; - - if (this._config.card_type === 'pop-up') { - return html` -
navigation_path
, or with the horizontal buttons stack that is included.
- The Bubble Card ${version} changelog is available here. -
If you have an issue or a question you can find more details on my GitHub page.
- -And if you like my project and want to support me, please consider making a donation. Any amount is welcome and very much appreciated! 🍻
- - ${this.makeVersion()} -data:
to your service.styles: |
, it will be added automatically.custom:bubble-pop-up
with custom:bubble-card
then add card_type: pop-up
. This mode will be removed around the v2.0.0.