Home-AssistantConfig/config/www/community/Bubble-Card/bubble-card.js

1441 lines
240 KiB
JavaScript
Raw Normal View History

2024-12-17 17:05:10 +00:00
(()=>{"use strict";var __webpack_modules__={946:(e,t,n)=>{function o(e,t=40){if(Array.isArray(e)&&3===e.length){for(let t=0;t<3;t++)if(e[t]<0||e[t]>255)return;return e.every((e=>Math.abs(e-255)<=t))}}let a;function i(e,t,n=1){if(e.startsWith("#"))if(4===e.length){let o=Math.min(255,parseInt(e.charAt(1).repeat(2),16)*n),i=Math.min(255,parseInt(e.charAt(2).repeat(2),16)*n),r=Math.min(255,parseInt(e.charAt(3).repeat(2),16)*n);a="rgba("+o+", "+i+", "+r+", "+t+")"}else{let o=Math.min(255,parseInt(e.slice(1,3),16)*n),i=Math.min(255,parseInt(e.slice(3,5),16)*n),r=Math.min(255,parseInt(e.slice(5,7),16)*n);a="rgba("+o+", "+i+", "+r+", "+t+")"}else if(e.startsWith("rgb")){let o=e.match(/\d+/g);a="rgba("+Math.min(255,o[0]*n)+", "+Math.min(255,o[1]*n)+", "+Math.min(255,o[2]*n)+", "+t+")"}else if(e.startsWith("var(--")){let o=e.slice(4,-1),r=window.getComputedStyle(document.documentElement).getPropertyValue(o);(r.startsWith("#")||r.startsWith("rgb"))&&(a=i(r,t,n))}return a}n.d(t,{_k:()=>i,wW:()=>o})},191:(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{BX:()=>fireEvent,GP:()=>applyScrollingEffect,IL:()=>getAttribute,Jn:()=>tapFeedback,OC:()=>isEntityType,P2:()=>throttle,Vv:()=>isColorLight,X:()=>getWeatherIcon,az:()=>createElement,gJ:()=>getImage,jk:()=>forwardHaptic,jx:()=>setLayout,mk:()=>getIconColor,o0:()=>formatDateTime,oY:()=>getName,pr:()=>isStateOn,q7:()=>getIcon,y0:()=>getState});var _style_ts__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(946);function hasStateChanged(e,t,n){if(e.hasState=t.states[n],e.hasState)return e.newState=[e.hasState.state,e.hasState.attributes.rgb_color],e.oldState&&e.newState[0]===e.oldState[0]&&e.newState[1]===e.oldState[1]?e.stateChanged=!1:(e.oldState=e.newState,e.stateChanged=!0),e.stateChanged}function configChanged(e,t){return!(!t.classList.contains("editor")||e.config===e.previousConfig||(e.previousConfig=e.config,0))}const fireEvent=(e,t,n,o)=>{o=o||{},n=null==n?{}:n;const a=new Event(t,{bubbles:void 0===o.bubbles||o.bubbles,cancelable:Boolean(o.cancelable),composed:void 0===o.composed||o.composed});return a.detail=n,e.dispatchEvent(a),a},forwardHaptic=e=>{fireEvent(window,"haptic",e)},navigate=(e,t,n=!1)=>{n?history.replaceState(null,"",t):history.pushState(null,"",t),fireEvent(window,"location-changed",{replace:n})};function toggleEntity(e,t){e.callService("homeassistant","toggle",{entity_id:t})}function tapFeedback(e){void 0!==e&&(e.style.display="",e.style.animation="tap-feedback .3s",setTimeout((()=>{e.style.animation="none",e.style.display="none"}),500))}function getIcon(e,t=e.config.entity,n=e.config.icon){const o=t?.split(".")[0],a=getAttribute(e,"device_class",t),i=getAttribute(e,"icon",t),r=n,s=getState(e,t),l={alarm_control_panel:"mdi:shield",alert:"mdi:alert",automation:"mdi:playlist-play",binary_sensor:function(){const n="off"===s;switch(getAttribute(e,"device_class",t)){case"battery":return n?"mdi:battery":"mdi:battery-outline";case"battery_charging":return n?"mdi:battery":"mdi:battery-charging";case"cold":return n?"mdi:thermometer":"mdi:snowflake";case"connectivity":return n?"mdi:server-network-off":"mdi:server-network";case"door":return n?"mdi:door-closed":"mdi:door-open";case"garage_door":return n?"mdi:garage":"mdi:garage-open";case"heat":return n?"mdi:thermometer":"mdi:fire";case"light":return n?"mdi:brightness-5":"mdi:brightness-7";case"lock":return n?"mdi:lock":"mdi:lock-open";case"moisture":return n?"mdi:water-off":"mdi:water";case"motion":return n?"mdi:motion-sensor-off":"mdi:motion-sensor";case"occupancy":case"presence":return n?"mdi:home-outline":"mdi:home";case"opening":return n?"mdi:square":"mdi:square-outline";case"plug":case"power":return n?"mdi:power-plug-off":"mdi:power-plug";case"running":return n?"mdi:stop":"mdi:play";case"safety":case"tamper":return n?"mdi:check-circle":"mdi:alert-circle";case"smoke":return n?"mdi:check-circle":"mdi:smoke";case"sound":return n?"mdi:music-note-off":"mdi:music-note";case"update":return n?"mdi:package":"mdi:package-up";case"vibration":return n?"mdi:crop-portrait":
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
<ha-textfield
label="Hash (e.g. #kitchen)"
.value="${this._hash}"
.configValue="${"hash"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:dock-top"></ha-icon>
Header settings
</h4>
<div class="content">
<ha-formfield .label="Optional - Show header">
<ha-switch
aria-label="Optional - Show header"
.checked=${this._show_header}
.configValue="${"show_header"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Show header</label>
</div>
</ha-formfield>
<ha-alert alert-type="info">You can completely hide the pop-up header, including the close button. To close it when hidden, either make a long swipe within the pop-up or click outside of it.</ha-alert>
<div style="${this._show_header?"":"display: none;"}">
<hr />
${this.makeDropdown("Button type","button_type",i)}
${this.makeDropdown("Optional - Entity","entity",n,r)}
<ha-textfield
label="Optional - Name"
.value="${this._name}"
.configValue="${"name"}"
@input="${this._valueChanged}"
></ha-textfield>
${this.makeDropdown("Optional - Icon","icon")}
${this.makeShowState()}
<hr />
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Tap action on icon
</h4>
<div class="content">
${this.makeTapActionPanel("Tap action")}
${this.makeTapActionPanel("Double tap action")}
${this.makeTapActionPanel("Hold action")}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined style="display: ${"slider"===this._config.button_type?"none":""}">
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Tap action on button
</h4>
<div class="content">
${this.makeTapActionPanel("Tap action",this._button_action,"name"!==this._config.button_type?"state"===this._config.button_type?"more-info":"toggle":"none","button_action")}
${this.makeTapActionPanel("Double tap action",this._button_action,"name"!==this._config.button_type?"state"===this._config.button_type?"more-info":"toggle":"none","button_action")}
${this.makeTapActionPanel("Hold action",this._button_action,"name"!==this._config.button_type?"more-info":"none","button_action")}
</div>
</ha-expansion-panel>
${this.makeSubButtonPanel()}
</div>
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:cog"></ha-icon>
Pop-up settings
</h4>
<div class="content">
<ha-textfield
label="Optional - Auto close in milliseconds (e.g. 15000)"
type="number"
inputMode="numeric"
min="0"
step="1000"
.value="${this._auto_close}"
.configValue="${"auto_close"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Slide to close distance (default to 400)"
type="number"
inputMode="numeric"
min="0"
step="10"
.value="${this._slide_to_close_distance}"
.configValue="${"slide_to_close_distance"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-formfield .label="Optional - Close the pop-up by clicking outside of it (a refresh is needed)">
<ha-switch
aria-label="Optional - Close the pop-up by clicking outside of it (a refresh is needed)"
.checked=${this._close_by_clicking_outside}
.configValue="${"close_by_clicking_outside"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Close the pop-up by clicking outside of it (a refresh is needed)</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Close the pop-up after any click or tap">
<ha-switch
aria-label="Optional - Close the pop-up after any click or tap"
.checked=${this._close_on_click}
.configValue="${"close_on_click"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Close the pop-up after any click or tap</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Update cards in background (not recommended)">
<ha-switch
aria-label="Optional - Update cards in background (not recommended)"
.checked=${this._background_update}
.configValue="${"background_update"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Update cards in background (not recommended)</label>
</div>
</ha-formfield>
<ha-alert alert-type="info">Background updates are only recommended if you encounter issues with certain cards within your pop-up.</ha-alert>
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:bell"></ha-icon>
Pop-up trigger
</h4>
<div class="content">
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${l}
@value-changed=${e=>this._conditionChanged(e)}
>
</ha-card-conditions-editor>
<ha-alert alert-type="info">
The pop-up will be opened when ALL conditions are fulfilled. For example you can open a "Security" pop-up with a camera when a person is in front of your house. You can also create a toggle helper (<code>input_boolean</code>) and trigger its opening/closing in an automation.
</ha-alert>
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Pop-up open/close action
</h4>
<div class="content">
${this.makeTapActionPanel("Open action",this._config,"none")}
${this.makeTapActionPanel("Close action",this._config,"none")}
<ha-alert alert-type="info">This allows you to trigger an action on pop-up open/close.</ha-alert>
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Styling options
</h4>
<div class="content">
${this.makeLayoutOptions()}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Pop-up styling
</h4>
<div class="content">
<ha-textfield
label="Optional - Margin (fix centering on some themes) (e.g. 13px)"
.value="${this._margin}"
.configValue="${"margin"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Top margin on mobile (e.g. -56px if your header is hidden)"
.value="${this._margin_top_mobile}"
.configValue="${"margin_top_mobile"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Top margin on desktop (e.g. 50vh for an half sized pop-up)"
.value="${this._margin_top_desktop}"
.configValue="${"margin_top_desktop"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Width on desktop (100% by default on mobile)"
.value="${this._width_desktop}"
.configValue="${"width_desktop"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Background color (any var, hex, rgb or rgba value)"
.value="${this._bg_color}"
.configValue="${"bg_color"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Background opacity (0-100 range)"
type="number"
inputMode="numeric"
min="0"
max="100"
.value="${this._bg_opacity}"
.configValue="${"bg_opacity"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Background blur (0-100 range)"
type="number"
inputMode="numeric"
min="0"
max="100"
.value="${this._bg_blur}"
.configValue="${"bg_blur"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Backdrop blur (0-100 range)"
type="number"
inputMode="numeric"
min="0"
max="100"
.value="${this._backdrop_blur}"
.configValue="${"backdrop_blur"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Shadow opacity (0-100 range)"
type="number"
inputMode="numeric"
min="0"
max="100"
.configValue="${"shadow_opacity"}"
.value="${this._shadow_opacity}""
@input="${this._valueChanged}"
></ha-textfield>
<ha-formfield .label="Optional - Hide pop-up backdrop (a refresh is needed)">
<ha-switch
aria-label="Optional - Hide pop-up backdrop (a refresh is needed)"
.checked=${this._hide_backdrop}
.configValue="${"hide_backdrop"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Hide pop-up backdrop (a refresh is needed)</label>
</div>
</ha-formfield>
<ha-alert alert-type="warning">Set this toggle to true on the first pop-up of your main dashboard to hide the darker backdrop behind all pop-ups. <b>You can add a blurred effect to it by changing <code>Optional - Backdrop blur</code> just below, but be aware that this can slow down your dashboard when opening pop-ups. It is now set to 0 for that reason.</b></ha-alert>
</div>
</ha-expansion-panel>
${this.makeStyleEditor()}
</div>
</ha-expansion-panel>
<ha-alert alert-type="info">
This card allows you to convert any vertical stack into a pop-up. Each pop-up is hidden by default and can be opened by targeting its link (e.g., '#pop-up-name'), with <a style="color: var(--text-primary-color)" href="https://github.com/Clooos/Bubble-Card#example">any card</a> that supports the <code>navigate</code> action, or with the <a style="color: var(--text-primary-color)" href="https://github.com/Clooos/Bubble-Card#horizontal-buttons-stack">horizontal buttons stack</a> that is included.
<br><br><b>Important:</b> This card must be placed within a <a style="color: var(--text-primary-color)" href="https://www.home-assistant.io/dashboards/vertical-stack/">vertical stack</a> card at the topmost position to function properly. To avoid misalignment with your view, place vertical stacks/pop-ups after all other dashboard cards. It should be called from the same view to work.
<br><br><b>You can also watch this <a style="color: var(--text-primary-color)" href="https://www.youtube.com/watch?v=7mOV7BfWoFc">video</a> that explains how to create your first pop-up.</b>
</ha-alert>
<ha-alert alert-type="warning">Since v1.7.0, the optimized mode has been removed to ensure stability and to simplify updates for everyone. However, if your pop-up content still appears on the screen during page loading, <a style="color: var(--text-primary-color)" href="https://github.com/Clooos/Bubble-Card#pop-up-initialization-fix">you can install this similar fix.</a></ha-alert>
${this.makeVersion()}
</div>
`}if("button"===this._config?.card_type)return ce`
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
${this.makeDropdown("Button type","button_type",i)}
${this.makeDropdown("slider"!==this._button_type?"Entity (toggle)":"Entity (light, media_player, cover or input_number)","entity",n,r)}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:cog"></ha-icon>
Button settings
</h4>
<div class="content">
<ha-textfield
label="Optional - Name"
.value="${this._name}"
.configValue="${"name"}"
@input="${this._valueChanged}"
></ha-textfield>
${this.makeDropdown("Optional - Icon","icon")}
${this.makeShowState()}
<ha-formfield .label="Optional - Slider live update" style="display: ${"slider"!==this._button_type?"none":""}">
<ha-switch
aria-label="Optional - Slider live update"
.checked=${this._slider_live_update}
.configValue="${"slider_live_update"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Slider live update</label>
</div>
</ha-formfield>
<ha-alert style="display: ${"slider"!==this._button_type?"none":""}" alert-type="info">By default, sliders are updated only on release. You can toggle this option to enable live updates while sliding.</ha-alert>
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Tap action on icon
</h4>
<div class="content">
${this.makeTapActionPanel("Tap action")}
${this.makeTapActionPanel("Double tap action")}
${this.makeTapActionPanel("Hold action")}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined style="display: ${"slider"===this._config.button_type?"none":""}">
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Tap action on button
</h4>
<div class="content">
${this.makeTapActionPanel("Tap action",this._button_action,"name"!==this._config.button_type?"state"===this._config.button_type?"more-info":"toggle":"none","button_action")}
${this.makeTapActionPanel("Double tap action",this._button_action,"name"!==this._config.button_type?"state"===this._config.button_type?"more-info":"toggle":"none","button_action")}
${this.makeTapActionPanel("Hold action",this._button_action,"name"!==this._config.button_type?"more-info":"none","button_action")}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Styling options
</h4>
<div class="content">
${this.makeLayoutOptions()}
${this.makeStyleEditor()}
</div>
</ha-expansion-panel>
${this.makeSubButtonPanel()}
<ha-alert alert-type="info">This card allows you to control your entities. ${"slider"===this._config.button_type?"Supported entities: Light (brightness), media player (volume), cover (position), fan (percentage), climate (temperature), input number and number (value). To access color / control of an entity, simply tap on the icon.":""}</ha-alert>
${this.makeVersion()}
</div>
`;if("separator"===this._config?.card_type)return ce`
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
<ha-textfield
label="Name"
.value="${this._name}"
.configValue="${"name"}"
@input="${this._valueChanged}"
></ha-textfield>
${this.makeDropdown("Icon","icon")}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Styling options
</h4>
<div class="content">
${this.makeLayoutOptions()}
${this.makeStyleEditor()}
</div>
</ha-expansion-panel>
${this.makeSubButtonPanel()}
<ha-alert alert-type="info">This card is a simple separator for dividing your pop-up into categories / sections. e.g. Lights, Devices, Covers, Settings, Automations...</ha-alert>
${this.makeVersion()}
</div>
`;if("horizontal-buttons-stack"===this._config?.card_type){if(!this.buttonAdded)for(this.buttonAdded=!0,this.buttonIndex=0;this._config[this.buttonIndex+1+"_link"];)this.buttonIndex++;function c(){this.buttonIndex++,this.requestUpdate()}return ce`
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
<div id="buttons-container">
${this.makeButton()}
</div>
<button class="icon-button" @click="${c}">
<ha-icon icon="mdi:plus"></ha-icon>
New button
</button>
<ha-formfield .label="Auto order">
<ha-switch
aria-label="Toggle auto order"
.checked=${this._auto_order}
.configValue="${"auto_order"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Auto order (Presence/occupancy sensors needed)</label>
</div>
</ha-formfield>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Styling options
</h4>
<div class="content">
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Horizontal buttons stack styling
</h4>
<div class="content">
<ha-textfield
label="Optional - Margin (fix centering on some themes) (e.g. 13px)"
.value="${this._margin}"
.configValue="${"margin"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Width on desktop (100% by default on mobile)"
.value="${this._width_desktop}"
.configValue="${"width_desktop"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-formfield .label="Optional - Rise animation (Displays an animation once the page has loaded)">
<ha-switch
aria-label="Optional - Rise animation (Displays an animation once the page has loaded)"
.checked=${this._rise_animation}
.configValue="${"rise_animation"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Rise animation (Displays an animation once the page has loaded)</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Highlight current hash / view">
<ha-switch
aria-label="Optional - Highlight current hash / view"
.checked=${this._highlight_current_view}
.configValue="${"highlight_current_view"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Highlight current hash / view</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Hide gradient">
<ha-switch
aria-label="Optional - Hide gradient"
.checked=${this._hide_gradient}
.configValue="${"hide_gradient"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Hide gradient</label>
</div>
</ha-formfield>
</div>
</ha-expansion-panel>
${this.makeStyleEditor()}
</div>
</ha-expansion-panel>
<ha-alert alert-type="info">This card is the companion to the pop-up card, allowing you to open the corresponding pop-ups. It also allows you to open any page of your dashboard. In addition, you can add your motion sensors so that the order of the buttons adapts according to the room you just entered. This card is scrollable, remains visible and acts as a footer.</ha-alert>
${this.makeVersion()}
</div>
`}if("cover"===this._config?.card_type)return ce`
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
${this.makeDropdown("Entity","entity",o)}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:cog"></ha-icon>
Cover settings
</h4>
<div class="content">
<ha-textfield
label="Optional - Name"
.value="${this._name||""}"
.configValue="${"name"}"
@input="${this._valueChanged}"
></ha-textfield>
${this.makeDropdown("Optional - Open icon","icon_open")}
${this.makeDropdown("Optional - Closed icon","icon_close")}
${this.makeShowState()}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:window-shutter-cog"></ha-icon>
Custom services
</h4>
<div class="content">
<ha-textfield
label="Optional - Open service (cover.open_cover by default)"
.value="${this._open_service}"
.configValue="${"open_service"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Stop service (cover.stop_cover by default)"
.value="${this._stop_service}"
.configValue="${"stop_service"}"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Close service (cover.close_cover by default)"
.value="${this._close_service}"
.configValue="${"close_service"}"
@input="${this._valueChanged}"
></ha-textfield>
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Tap action on icon
</h4>
<div class="content">
${this.makeTapActionPanel("Tap action")}
${this.makeTapActionPanel("Double tap action")}
${this.makeTapActionPanel("Hold action")}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Styling options
</h4>
<div class="content">
${this.makeLayoutOptions()}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Cover styling
</h4>
<div class="content">
${this.makeDropdown("Optional - Arrow down icon","icon_down")}
${this.makeDropdown("Optional - Arrow up icon","icon_up")}
</div>
</ha-expansion-panel>
${this.makeStyleEditor()}
</div>
</ha-expansion-panel>
${this.makeSubButtonPanel()}
<ha-alert alert-type="info">This card allows you to control your covers.</ha-alert>
${this.makeVersion()}
</div>
`;if("media-player"===this._config?.card_type)return ce`
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
${this.makeDropdown("Entity","entity",this.mediaPlayerList)}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:cog"></ha-icon>
Media player settings
</h4>
<div class="content">
<ha-textfield
label="Optional - Name"
.value="${this._name||""}"
.configValue="${"name"}"
@input="${this._valueChanged}"
></ha-textfield>
${this.makeDropdown("Optional - Icon","icon")}
${this.makeShowState()}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:eye-off"></ha-icon>
Display/hide buttons
</h4>
<div class="content">
<ha-formfield .label="Optional - Hide play/pause button">
<ha-switch
aria-label="Optional - Hide play/pause button"
.checked=${this._hide_play_pause_button}
.configValue="${"hide.play_pause_button"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Hide play/pause button</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Hide volume button">
<ha-switch
aria-label="Optional - Hide volume button"
.checked=${this._hide_volume_button}
.configValue="${"hide.volume_button"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Hide volume button</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Hide next button">
<ha-switch
aria-label="Optional - Hide next button"
.checked=${this._hide_next_button}
.configValue="${"hide.next_button"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Hide next button</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Hide previous button">
<ha-switch
aria-label="Optional - Hide previous button"
.checked=${this._hide_previous_button}
.configValue="${"hide.previous_button"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Hide previous button</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Hide power button">
<ha-switch
aria-label="Optional - Hide power button"
.checked=${this._hide_power_button}
.configValue="${"hide.power_button"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Hide power button</label>
</div>
</ha-formfield>
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Tap action on icon
</h4>
<div class="content">
${this.makeTapActionPanel("Tap action")}
${this.makeTapActionPanel("Double tap action")}
${this.makeTapActionPanel("Hold action")}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Styling options
</h4>
<div class="content">
${this.makeLayoutOptions()}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Media player styling
</h4>
<div class="content">
<ha-formfield .label="Optional - Blurred media cover in background">
<ha-switch
aria-label="Optional - Blurred media cover in background"
.checked=${this._cover_background}
.configValue="${"cover_background"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Blurred media cover in background</label>
</div>
</ha-formfield>
</div>
</ha-expansion-panel>
${this.makeStyleEditor()}
</div>
</ha-expansion-panel>
${this.makeSubButtonPanel()}
<ha-alert alert-type="info">This card allows you to control a media player. You can tap on the icon to get more control.</ha-alert>
${this.makeVersion()}
</div>
`;if("empty-column"===this._config?.card_type)return ce`
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Styling options
</h4>
<div class="content">
${this.makeLayoutOptions()}
</div>
</ha-expansion-panel>
<ha-alert alert-type="info">Just an empty card to fill any empty column.</ha-alert>
${this.makeVersion()}
</div>
`;if("select"===this._config?.card_type){const d=this._config.entity,u=(d?.startsWith("input_select")||d?.startsWith("select")||this._config.select_attribute,this.hass.states[d]?.attributes),b=this._selectable_attributes.some((e=>u?.[e])),p=Object.keys(this.hass.states[d]?.attributes||{}).map((e=>{let t=this.hass.states[d];return{label:this.hass.formatEntityAttributeName(t,e),value:e}})).filter((e=>this._selectable_attributes.includes(e.value)));return ce`
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
${this.makeDropdown("Entity","entity",this.inputSelectList)}
${b?ce`
<div class="ha-combo-box">
<ha-combo-box
label="Select menu (from attributes)"
.value="${this._config.select_attribute}"
.items="${p}"
.configValue="${"select_attribute"}"
@value-changed="${this._valueChanged}"
></ha-combo-box>
</div>
`:""}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:cog"></ha-icon>
Button settings
</h4>
<div class="content">
<ha-textfield
label="Optional - Name"
.value="${this._name}"
.configValue="${"name"}"
@input="${this._valueChanged}"
></ha-textfield>
${this.makeDropdown("Optional - Icon","icon")}
${this.makeShowState()}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Tap action on icon
</h4>
<div class="content">
${this.makeTapActionPanel("Tap action")}
${this.makeTapActionPanel("Double tap action")}
${this.makeTapActionPanel("Hold action")}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Styling options
</h4>
<div class="content">
${this.makeLayoutOptions()}
${this.makeStyleEditor()}
</div>
</ha-expansion-panel>
${this.makeSubButtonPanel()}
<ha-alert alert-type="info">
This card allows you to have a select menu for your
<code>input_select</code>, <code>select</code> entities, and
any other entities that have attribute lists like
<code>source_list</code>, <code>sound_mode_list</code>,
<code>hvac_modes</code>, <code>fan_modes</code>,
<code>swing_modes</code>, <code>preset_modes</code>, or
<code>effect_list</code>.
</ha-alert>
${this.makeVersion()}
</div>
`}if("climate"===this._config?.card_type){if("climate"===this._config.card_type&&!this.climateSubButtonsAdded&&this._config.entity){const h=this.hass.states[this._config.entity]?.attributes?.hvac_modes;this._config.sub_button&&0!==this._config.sub_button.length||(this._config.sub_button=[h?{name:"HVAC modes menu",select_attribute:"hvac_modes",state_background:!1,show_arrow:!1}:null].filter(Boolean)),this.climateSubButtonsAdded=!0}return ce`
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
${this.makeDropdown("Entity","entity",this.climateList)}
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:cog"></ha-icon>
Climate settings
</h4>
<div class="content">
<ha-textfield
label="Optional - Name"
.value="${this._name}"
.configValue="${"name"}"
@input="${this._valueChanged}"
></ha-textfield>
${this.makeDropdown("Optional - Icon","icon")}
${this.makeShowState()}
${this.hass.states[this._config.entity]?.attributes?.target_temp_low?ce`
<ha-formfield .label="Optional - Hide target temp low">
<ha-switch
aria-label="Optional - Hide target temp low"
.checked=${this._config.hide_target_temp_low}
.configValue="${"hide_target_temp_low"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Hide target temp low</label>
</div>
</ha-formfield>
`:""}
${this.hass.states[this._config.entity]?.attributes?.target_temp_high?ce`
<ha-formfield .label="Optional - Hide target temp high">
<ha-switch
aria-label="Optional - Hide target temp high"
.checked=${this._config.hide_target_temp_high}
.configValue="${"hide_target_temp_high"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Hide target temp high</label>
</div>
</ha-formfield>
`:""}
<ha-formfield .label="Optional - Constant background color when ON">
<ha-switch
aria-label="Optional - Constant background color when ON"
.checked=${!0===this._config.state_color}
.configValue="${"state_color"}"
@change=${this._valueChanged}
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Constant background color when ON</label>
</div>
</ha-formfield>
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Tap action on icon
</h4>
<div class="content">
${this.makeTapActionPanel("Tap action")}
${this.makeTapActionPanel("Double tap action")}
${this.makeTapActionPanel("Hold action")}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:palette"></ha-icon>
Styling options
</h4>
<div class="content">
${this.makeLayoutOptions()}
${this.makeStyleEditor()}
</div>
</ha-expansion-panel>
${this.makeSubButtonPanel()}
<ha-alert alert-type="info">This card allows you to control your climate entities. You can also add a sub-button that display a select menu for your climate modes (check if you have "Select menu" available when you create a new sub-button).</ha-alert>
${this.makeVersion()}
</div>
`}return this._config?.card_type?void 0:ce`
<div class="card-config">
${this.makeDropdown("Card type","card_type",a)}
<ha-alert alert-type="info">You need to add a card type first. Please note that in some cases, a page refresh might be needed after exiting the editor.</ha-alert>
<img style="width: 100%; height: auto; border-radius: 24px;" src="https://raw.githubusercontent.com/Clooos/Bubble-Card/main/.github/bubble-card.gif">
<p>The <b>Bubble Card ${e}</b> changelog is available <a href="https://github.com/Clooos/Bubble-Card/releases/tag/${e}"><b>here</b></a>.</p>
<hr />
<p>If you have an issue or a question you can find more details in the GitHub documentation. You can also find useful resources and help in these links.</p>
<div style="display: inline-block;">
<a href="https://github.com/Clooos/Bubble-Card"><img src="https://img.shields.io/badge/GitHub-Documentation-blue?logo=github"></a>
<a href="https://www.youtube.com/@cloooos"><img src="https://img.shields.io/badge/YouTube-My%20channel-red?logo=youtube"></a>
<a href="https://www.reddit.com/r/BubbleCard/"><img src="https://img.shields.io/badge/Reddit-r/BubbleCard-orange?logo=reddit"></a>
<a href="https://community.home-assistant.io/t/bubble-card-a-minimalist-card-collection-for-home-assistant-with-a-nice-pop-up-touch/609678"><img src="https://img.shields.io/badge/Home%20Assistant-Community%20Forum-blue?logo=home-assistant"></a>
</div>
<hr />
<p>I dedicate most of my spare time to making this project the best it can be. So if you appreciate my work, any donation would be a great way to show your support.</p>
<div style="display: inline-block;">
<a href="https://www.buymeacoffee.com/clooos"><img src="https://img.shields.io/badge/Donate-Buy%20me%20a%20beer-yellow?logo=buy-me-a-coffee"></a>
<a href="https://www.paypal.com/donate/?business=MRVBV9PLT9ZPL&no_recurring=0&item_name=Hi%2C+I%27m+Clooos+the+creator+of+Bubble+Card.+Thank+you+for+supporting+me+and+my+passion.+You+are+awesome%21+%F0%9F%8D%BB&currency_code=EUR"><img src="https://img.shields.io/badge/Donate-PayPal-blue?logo=paypal"></img></a>
</div>
<p>Looking for more advanced examples? Check out my <a href="https://www.patreon.com/Clooos"><b>Patreon</b></a> for exclusive custom styles and templates!</p>
<a href="https://www.patreon.com/Clooos"><img src="https://img.shields.io/badge/Patreon-Clooos-orange?logo=patreon"></a>
<p style="margin-top: 0;">Thank you! 🍻</p>
${this.makeVersion()}
</div>
`}makeLayoutOptions(){return ce`
<ha-combo-box
label="${"pop-up"===this._config.card_type?"Header card layout":"Card layout"}"
.value="${this._config.card_layout||"normal"}"
.configValue="${"card_layout"}"
.items="${[{label:"Normal",value:"normal"},{label:"Large (Optimized for sections)",value:"large"},{label:"Large with 2 sub-buttons rows (Optimized for sections)",value:"large-2-rows"}]}"
@value-changed="${this._valueChanged}"
></ha-combo-box>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:table"></ha-icon>
Layout options for sections
</h4>
<div class="content">
<ha-combo-box
label="Columns"
.value="${this._config.columns}"
.configValue="${"columns"}"
.items="${[{label:"Auto",value:null},{label:"1/4",value:1},{label:"2/4",value:2},{label:"3/4",value:3},{label:"4/4",value:4}]}"
@value-changed="${this._valueChanged}"
></ha-combo-box>
<ha-combo-box
label="Rows"
.value="${this._config.rows}"
.configValue="${"rows"}"
.items="${[{label:"Auto",value:null},{label:"1/4",value:1},{label:"2/4",value:2},{label:"3/4",value:3},{label:"4/4",value:4}]}"
@value-changed="${this._valueChanged}"
></ha-combo-box>
</div>
</ha-expansion-panel>
`}makeShowState(e=this._config,t="",n=!1,o){const a=e?.entity??this._config.entity??"",i="name"===this._config.button_type,r=a?.startsWith("input_select")||a?.startsWith("select")||e.select_attribute,s=Object.keys(this.hass.states[a]?.attributes||{}).map((e=>{let t=this.hass.states[a];return{label:this.hass.formatEntityAttributeName(t,e),value:e}}));return ce`
${"sub_button"!==n?ce`
<ha-formfield .label="Optional - Text scrolling effect">
<ha-switch
aria-label="Optional - Text scrolling effect"
.checked=${e?.scrolling_effect??!0}
.configValue="${t+"scrolling_effect"}"
@change="${n?e=>this._arrayValueChange(o,{scrolling_effect:e.target.checked},n):this._valueChanged}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Text scrolling effect</label>
</div>
</ha-formfield>
`:""}
${"sub_button"===n?ce`
<ha-formfield .label="Optional - Show background">
<ha-switch
aria-label="Optional - Show background when entity is on"
.checked=${e?.show_background??!0}
@change="${e=>this._arrayValueChange(o,{show_background:e.target.checked},n)}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Show background when entity is on</label>
</div>
</ha-formfield>
`:""}
${"sub_button"===n&&(e?.show_background??1)?ce`
<ha-formfield .label="Optional - Background color based on state">
<ha-switch
aria-label="Optional - Background color based on state"
.checked=${e?.state_background??!0}
@change="${e=>this._arrayValueChange(o,{state_background:e.target.checked},n)}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Background color based on state</label>
</div>
</ha-formfield>
`:""}
${"sub_button"===n&&(e?.state_background??1)&&a.startsWith("light")?ce`
<ha-formfield .label="Optional - Background color based on light color">
<ha-switch
aria-label="Optional - Background color based on light color"
.checked=${e?.light_background??!0}
@change="${e=>this._arrayValueChange(o,{light_background:e.target.checked},n)}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Background color based on light color</label>
</div>
</ha-formfield>
`:""}
${"sub_button"!==n&&a.startsWith("light")?ce`
<ha-formfield .label="Optional - Use accent color instead of light color">
<ha-switch
aria-label="Optional - Use accent color instead of light color"
.checked=${e?.use_accent_color??!1}
.configValue="${t+"use_accent_color"}"
@change="${this._valueChanged}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Use accent color instead of light color</label>
</div>
</ha-formfield>
`:""}
<ha-formfield .label="Optional - Show icon">
<ha-switch
aria-label="Optional - Show icon"
.checked=${e?.show_icon??!0}
.configValue="${t+"show_icon"}"
@change="${n?e=>this._arrayValueChange(o,{show_icon:e.target.checked},n):this._valueChanged}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Show icon</label>
</div>
</ha-formfield>
${"sub_button"!==n?ce`
<ha-formfield .label="Optional - Prioritize icon over entity picture">
<ha-switch
aria-label="Optional - Prioritize icon over entity picture"
.checked=${e?.force_icon??!1}
.configValue="${t+"force_icon"}"
.disabled="${i}"
@change="${n?e=>this._arrayValueChange(o,{force_icon:e.target.checked},n):this._valueChanged}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Prioritize icon over entity picture</label>
</div>
</ha-formfield>
`:""}
<ha-formfield .label="Optional - Show name">
<ha-switch
aria-label="Optional - Show name"
.checked=${!!(e?.show_name??"sub_button"!==n)}
.configValue="${t+"show_name"}"
@change="${n?e=>this._arrayValueChange(o,{show_name:e.target.checked},n):this._valueChanged}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Show name</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Show entity state">
<ha-switch
aria-label="Optional - Show entity state"
.checked="${e?.show_state??"state"===e.button_type}"
.configValue="${t+"show_state"}"
.disabled="${i&&"sub_button"!==n}"
@change="${n?e=>this._arrayValueChange(o,{show_state:e.target.checked},n):this._valueChanged}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Show entity state</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Show last changed">
<ha-switch
aria-label="Optional - Show last changed"
.checked=${e?.show_last_changed}
.configValue="${t+"show_last_changed"}"
.disabled="${i&&"sub_button"!==n}"
@change="${n?e=>this._arrayValueChange(o,{show_last_changed:e.target.checked},n):this._valueChanged}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Show last changed</label>
</div>
</ha-formfield>
<ha-formfield .label="Optional - Show attribute">
<ha-switch
aria-label="Optional - Show attribute"
.checked=${e?.show_attribute}
.configValue="${t+"show_attribute"}"
.disabled="${i&&"sub_button"!==n}"
@change="${n?e=>this._arrayValueChange(o,{show_attribute:e.target.checked},n):this._valueChanged}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Show attribute</label>
</div>
</ha-formfield>
${e?.show_attribute?ce`
<div class="ha-combo-box">
<ha-combo-box
label="Optional - Attribute to show"
.value="${e?.attribute}"
.configValue="${t+"attribute"}"
.items="${s}"
.disabled="${i}"
@value-changed="${n?e=>this._arrayValueChange(o,{attribute:e.detail.value},n):this._valueChanged}"
></ha-combo-box>
</div>
`:""}
${"sub_button"===n&&r?ce`
<ha-formfield .label="Optional - Show arrow (Select entities only)">
<ha-switch
aria-label="Optional - Show arrow (Select entities only)"
.checked=${e?.show_arrow??!0}
.configValue="${t+"show_arrow"}"
@change="${n?e=>this._arrayValueChange(o,{show_arrow:e.target.checked},n):this._valueChanged}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Show arrow (Select menu only)</label>
</div>
</ha-formfield>
`:""}
`}makeDropdown(e,t,n,o){return e.includes("icon")||e.includes("Icon")?ce`
<div class="ha-icon-picker">
<ha-icon-picker
label="${e}"
.value="${this["_"+t]}"
.configValue="${t}"
item-value-path="icon"
item-label-path="icon"
@value-changed="${this._valueChanged}"
></ha-icon-picker>
</div>
`:ce`
<div class="ha-combo-box">
<ha-combo-box
label="${e}"
.value="${this["_"+t]}"
.configValue="${t}"
.items="${n}"
.disabled="${o}"
@value-changed="${this._valueChanged}"
></ha-combo-box>
</div>
`}makeTapActionPanel(e,t=this._config,n,o,a=this._config){this.hass;const i="Tap action"===e?"mdi:gesture-tap":"Double tap action"===e?"mdi:gesture-double-tap":"Hold action"===e?"mdi:gesture-tap-hold":"mdi:gesture-tap",r="Tap action"===e?t.tap_action:"Double tap action"===e?t.double_tap_action:"Hold action"===e?t.hold_action:"Open action"===e?t.open_action:t.close_action,s="Tap action"===e?"tap_action":"Double tap action"===e?"double_tap_action":"Hold action"===e?"hold_action":"Open action"===e?"open_action":"close_action",l=t===this._config;return n||(n=l&&"Tap action"===e?"name"!==this._config.button_type?"more-info":"none":l?"name"!==this._config.button_type?"toggle":"none":""),ce`
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="${i}"></ha-icon>
${e}
</h4>
<div class="content">
<div class="ha-combo-box">
<ha-combo-box
label="${e}"
.value="${r?.action??n}"
.items="${this.tapActionTypeList}"
@value-changed="${e=>this._tapActionValueChange(a,{[s]:{action:e.detail.value}},o)}"
></ha-combo-box>
</div>
${"navigate"===r?.action?ce`
<div class="ha-textfield">
<ha-textfield
label="Navigation path"
.value="${r?.navigation_path??""}"
@input="${e=>this._tapActionValueChange(a,{[s]:{navigation_path:e.target.value}},o)}"
></ha-textfield>
</div>
`:""}
${"url"===r?.action?ce`
<div class="ha-textfield">
<ha-textfield
label="URL path"
.value="${r?.url_path??""}"
@input="${e=>this._tapActionValueChange(a,{[s]:{url_path:e.target.value}},o)}"
></ha-textfield>
</div>
`:""}
${"call-service"===r?.action?ce`
<div class="ha-textfield">
<ha-textfield
label="Service"
.value="${r?.service??""}"
@input="${e=>this._tapActionValueChange(a,{[s]:{service:e.target.value}},o)}"
></ha-textfield>
</div>
<div class="ha-combo-box">
<ha-combo-box
label="Optional - Entity"
.value="${r?.target?.entity_id}"
.items="${this.allEntitiesList}"
@value-changed="${"entity"!==r?.target?.entity_id?e=>{this._tapActionValueChange(a,{[s]:{target:{entity_id:e.detail.value}}},o)}:""}"
></ha-combo-box>
</div>
<ha-formfield .label="Optional - Use default entity">
<ha-switch
aria-label="Optional - Use default entity"
.checked=${"entity"===r?.target?.entity_id}
@change="${e=>{"entity"!==r?.target?.entity_id?this._tapActionValueChange(a,{[s]:{target:{entity_id:"entity"}}},o):this._tapActionValueChange(a,{[s]:{target:{}}},o)}}"
></ha-switch>
<div class="mdc-form-field">
<label class="mdc-label">Optional - Use default entity</label>
</div>
</ha-formfield>
`:""}
${"call-service"===r?.action&&r?.service?ce`
<ha-alert alert-type="info">For now, you still need to switch to the YAML editor if you want to add <code>data:</code> to your service.</ha-alert>
`:""}
</div>
</ha-expansion-panel>
`}makeSubButtonPanel(){const e=this._config?.sub_button?.map(((e,t)=>{if(!e)return;const n="sub_button."+t+".",o=e.entity??this._config.entity,a=o?.startsWith("input_select")||o?.startsWith("select")||e.select_attribute,i=this.hass.states[o]?.attributes,r=this._selectable_attributes.some((e=>i?.[e])),s=Object.keys(this.hass.states[o]?.attributes||{}).map((e=>{let t=this.hass.states[o];return{label:this.hass.formatEntityAttributeName(t,e),value:e}})).filter((e=>this._selectable_attributes.includes(e.value))),l=e.visibility??[];return ce`
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:border-radius"></ha-icon>
${this._config.sub_button[t]?"Button "+(t+1)+(e.name?" - "+e.name:""):"New button"}
<button class="icon-button header" @click="${n=>{n.stopPropagation();let o=[...this._config.sub_button];o.splice(t,1),this._config.sub_button=o,this._valueChanged({target:{configValue:"sub_button."+(t-1),value:e}}),this.requestUpdate()}}">
<ha-icon icon="mdi:delete"></ha-icon>
</button>
${t>0?ce`<button class="icon-button header" @click="${e=>{if(e.stopPropagation(),t>0){let e=[...this._config.sub_button];[e[t],e[t-1]]=[e[t-1],e[t]],this._config.sub_button=e,this._valueChanged({target:{configValue:"sub_button."+t,value:this._config.sub_button[t]}})}this.requestUpdate()}}">
<ha-icon icon="mdi:arrow-left"></ha-icon>
</button>`:""}
${t<this._config.sub_button.length-1?ce`<button class="icon-button header" @click="${e=>{if(e.stopPropagation(),t<this._config.sub_button.length-1){let e=[...this._config.sub_button];[e[t],e[t+1]]=[e[t+1],e[t]],this._config.sub_button=e,this._valueChanged({target:{configValue:"sub_button."+t,value:this._config.sub_button[t]}})}this.requestUpdate()}}">
<ha-icon icon="mdi:arrow-right"></ha-icon>
</button>`:""}
</h4>
<div class="content">
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:cog"></ha-icon>
Button settings
</h4>
<div class="content">
<div class="ha-combo-box">
<ha-combo-box
label="${"Optional - Entity (default to card entity)"}"
.value="${o}"
.items="${this.allEntitiesList}"
@value-changed="${e=>this._arrayValueChange(t,{entity:e.detail.value},"sub_button")}"
></ha-combo-box>
</div>
${r?ce`
<div class="ha-combo-box">
<ha-combo-box
label="Optional - Select menu (from attributes)"
.value="${e.select_attribute}"
.items="${s}"
@value-changed="${e=>this._arrayValueChange(t,{select_attribute:e.detail.value},"sub_button")}"
></ha-combo-box>
</div>
`:""}
<div class="ha-textfield">
<ha-textfield
label="Optional - Name"
.value="${e.name??""}"
@input="${e=>this._arrayValueChange(t,{name:e.target.value},"sub_button")}"
></ha-textfield>
</div>
<div class="ha-icon-picker">
<ha-icon-picker
label="Optional - Icon"
.value="${e.icon}"
item-label-path="label"
item-value-path="value"
@value-changed="${e=>this._arrayValueChange(t,{icon:e.detail.value},"sub_button")}"
></ha-icon-picker>
</div>
${this.makeShowState(e,n,"sub_button",t)}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined style="${a?"opacity: 0.5; pointer-events: none;":""}">
<h4 slot="header">
<ha-icon icon="mdi:gesture-tap"></ha-icon>
Tap action on button
</h4>
<div class="content">
${this.makeTapActionPanel("Tap action",e,"more-info","sub_button",t)}
${this.makeTapActionPanel("Double tap action",e,"none","sub_button",t)}
${this.makeTapActionPanel("Hold action",e,"none","sub_button",t)}
</div>
</ha-expansion-panel>
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:eye"></ha-icon>
Visibility
</h4>
<div class="content">
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${l}
@value-changed=${e=>this._conditionChanged(e,t,"sub_button")}
>
</ha-card-conditions-editor>
<ha-alert alert-type="info">
The sub-button will be shown when ALL conditions are fulfilled. If no conditions are set, the sub-button will always be shown.
</ha-alert>
</div>
</ha-expansion-panel>
</div>
</ha-expansion-panel>
`}));return ce`
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:shape-square-rounded-plus"></ha-icon>
Sub-buttons editor
</h4>
<div class="content">
${e}
<button class="icon-button" @click="${()=>{this._config.sub_button||(this._config.sub_button=[]);let e={entity:this._config.entity};this._config.sub_button=[...this._config.sub_button],this._config.sub_button.push(e),(0,n.BX)(this,"config-changed",{config:this._config}),this.requestUpdate()}}">
<ha-icon icon="mdi:plus"></ha-icon>
New sub-button
</button>
<ha-alert alert-type="info">
Add new customized buttons fixed to the right.
These buttons can also display a select menu for your
<code>input_select</code>, <code>select</code> entities, and
any other entities that have attribute lists like
<code>source_list</code>, <code>sound_mode_list</code>,
<code>hvac_modes</code>, <code>fan_modes</code>,
<code>swing_modes</code>, <code>preset_modes</code>, or
<code>effect_list</code>.
</ha-alert>
</div>
</ha-expansion-panel>
`}makeButton(){let e=[];for(let t=1;t<=this.buttonIndex;t++)e.push(ce`
<div class="${t}_button">
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:border-radius"></ha-icon>
Button ${t} ${this._config[t+"_name"]?"- "+this._config[t+"_name"]:""}
<button class="icon-button header" @click="${()=>this.removeButton(t)}">
<ha-icon icon="mdi:delete"></ha-icon>
</button>
</h4>
<div class="content">
<ha-textfield
label="Link / Hash to pop-up (e.g. #kitchen)"
.value="${this._config[t+"_link"]||""}"
.configValue="${t}_link"
@input="${this._valueChanged}"
></ha-textfield>
<ha-textfield
label="Optional - Name"
.value="${this._config[t+"_name"]||""}"
.configValue="${t}_name"
@input="${this._valueChanged}"
></ha-textfield>
<ha-icon-picker
label="Optional - Icon"
.value="${this._config[t+"_icon"]||""}"
.configValue="${t}_icon"
item-label-path="label"
item-value-path="value"
@value-changed="${this._valueChanged}"
></ha-icon-picker>
<ha-combo-box
label="Optional - Light / Light group (For background color)"
.value="${this._config[t+"_entity"]||""}"
.configValue="${t}_entity"
.items="${this.allEntitiesList}"
@value-changed="${this._valueChanged}"
></ha-combo-box>
<ha-combo-box
label="Optional - Presence / Occupancy sensor (For button auto order)"
.value="${this._config[t+"_pir_sensor"]||""}"
.configValue="${t}_pir_sensor"
.disabled=${!this._config.auto_order}
.items="${this.allEntitiesList}"
@value-changed="${this._valueChanged}"
></ha-combo-box>
<ha-alert alert-type="info">In fact you can also get the auto order with any entity type, for example you can add light groups to these fields and the order will change based on the last changed states.</ha-alert>
</div>
</ha-expansion-panel>
</div>
`);return e}makeVersion(){return ce`
<h4 style="
font-size: 12px !important;
color: #fff;
background: rgba(0,0,0,0.1);
padding: 8px 16px;
border-radius: 32px;
">
Bubble Card
<span style="
font-size: 10px;
background: rgba(0,120,180,1);
padding: 0px 8px;
border-radius: 12px;
margin-right: -6px;
float: right;
color: white;
">
${e}
</span>
</h4>
`}removeButton(e){delete this._config[e+"_name"],delete this._config[e+"_icon"],delete this._config[e+"_link"],delete this._config[e+"_entity"],delete this._config[e+"_pir_sensor"];for(let t=e;t<this.buttonIndex;t++)this._config[t+"_name"]=this._config[t+1+"_name"],this._config[t+"_icon"]=this._config[t+1+"_icon"],this._config[t+"_link"]=this._config[t+1+"_link"],this._config[t+"_entity"]=this._config[t+1+"_entity"],this._config[t+"_pir_sensor"]=this._config[t+1+"_pir_sensor"];delete this._config[this.buttonIndex+"_name"],delete this._config[this.buttonIndex+"_icon"],delete this._config[this.buttonIndex+"_link"],delete this._config[this.buttonIndex+"_entity"],delete this._config[this.buttonIndex+"_pir_sensor"],this.buttonIndex--,(0,n.BX)(this,"config-changed",{config:this._config})}makeStyleEditor(){return ce`
<ha-expansion-panel outlined>
<h4 slot="header">
<ha-icon icon="mdi:code-braces"></ha-icon>
Custom styles / Templates
</h4>
<div class="content">
<div class="code-editor">
<ha-code-editor
mode="yaml"
autofocus
autocomplete-entities
autocomplete-icons
.hass=${this.hass}
.value=${this._config.styles}
.configValue="${"styles"}"
@value-changed=${this._valueChanged}
></ha-code-editor>
</div>
<ha-alert alert-type="info">
For advanced users, you can edit the CSS style of this card in this editor. More information <a href="https://github.com/Clooos/Bubble-Card#styling">here</a>. You don't need to add <code>styles: |</code>, it will be added automatically. You can also add <a href="https://github.com/Clooos/Bubble-Card#templates">templates</a>.
<br><br><b>Looking for more advanced examples?</b> Check out my <a href="https://www.patreon.com/Clooos">Patreon</a> for exclusive custom styles and advanced templates, this is also the best way to show your support to my project!
</ha-alert>
</div>
</ha-expansion-panel>
`}_valueChanged(e){const t=e.target,o=e.detail;let a;if("HA-SWITCH"===t.tagName?a=t.checked:void 0!==t.value&&(a="string"==typeof t.value?t.value.replace(",","."):t.value),"string"==typeof a&&(a.endsWith(".")||"-"===a))return;const{configValue:i,checked:r}=t;if(i){const n=i.split(".");let r=this._config;for(let e=0;e<n.length-1;e++)r[n[e]]=r[n[e]]||{},r=r[n[e]];"input"===e.type?r[n[n.length-1]]=a:o&&r[n[n.length-1]]!==o.value?r[n[n.length-1]]=o.value:"HA-SWITCH"===t.tagName&&(r[n[n.length-1]]=a)}(0,n.BX)(this,"config-changed",{config:this._config}),this.requestUpdate()}_arrayValueChange(e,t,o){if(this._config.sub_button&&!this.subButtonJustAdded)return this.subButtonJustAdded=!0,void setTimeout((()=>this._arrayValueChange(e,t,o)),10);this._config[o]=this._config[o]||[];let a=[...this._config[o]];a[e]=a[e]||{},a[e]={...a[e],...t},this._config[o]=a,(0,n.BX)(this,"config-changed",{config:this._config}),this.requestUpdate()}_tapActionValueChange(e,t,o){if(void 0===o)for(let e in t)this._config[e]={...this._config[e],...t[e]};else{this._config[o]=this._config[o]||(o?{}:[]);let n=Array.isArray(this._config[o])?[...this._config[o]]:{...this._config[o]};if(Array.isArray(n)){n[e]=n[e]||{};let o={...n[e]};for(let e in t)o[e]=e in o?{...o[e],...t[e]}:t[e];n[e]=o}else for(let e in t)n.hasOwnProperty(e)?n[e]={...n[e],...t[e]}:n[e]=t[e];this._config[o]=n}(0,n.BX)(this,"config-changed",{config:this._config}),this.requestUpdate()}_conditionChanged(e,t,o){if(e.stopPropagation(),o){this._config[o]=this._config[o]||[];let n=[...this._config[o]];n[t]=n[t]||{};const a=e.detail.value;n[t]={...n[t],visibility:a},this._config[o]=n}else if("pop-up"===this._config.card_type){const t=e.detail.value;this._config={...this._config,trigger:t}}(0,n.BX)(this,"config-changed",{config:this._config}),this.requestUpdate()}static get styles(){return de`
div {
display: grid;
grid-gap: 12px;
}
ha-combo-box[label="Card type"]::after {
content: "";
position: relative;
background-color: var(--background-color, var(--secondary-background-color));
display: block;
width: 100%;
height: 1px;
top: 12px;
margin-bottom: 12px !important;
opacity: 0.6;
}
#add-button {
margin: 0 0 14px 0;
color: var(--text-primary-color);
width: 100%;
height: 32px;
border-radius: 16px;
border: none;
background-color: var(--accent-color);
cursor: pointer;
}
p {
margin-bottom: 4px;
}
ha-icon, a, p, button, h4 {
color: var(--primary-text-color) !important;
}
hr {
display: inline-block;
width: 100%;
border: 1px solid var(--background-color, var(--secondary-background-color));
opacity: 0.6;
margin: 8px 0 0 0;
}
code {
background: var(--accent-color);
background-blend-mode: darken;
padding: 2px 4px;
border-radius: 6px;
}
.button-header {
height: auto;
width: 100%;
display: inline-flex;
align-items: center;
margin: 0 8px;
}
.button-number {
display: inline-flex;
width: auto;
}
.remove-button {
display: inline-flex;
border-radius: 50%;
width: 24px;
height: 24px;
text-align: center;
line-height: 24px;
vertical-align: middle;
cursor: pointer;
}
.content {
margin: 12px 4px 14px 4px;
}
h4 > ha-icon {
margin: 8px;
}
ha-textfield {
width: 100%;
}
h3 {
margin: 4px 0;
}
.code-editor {
overflow: scroll;
}
.icon-button {
background: var(--accent-color);
border: none;
cursor: pointer;
padding: 8px;
margin: 0;
border-radius: 32px;
font-weight: bold;
}
.icon-button.header {
background: none;
float: right;
padding: 0;
margin: 0 8px;
}
ha-card-conditions-editor {
margin-top: -12px;
}
`}})}(),document.createElement("bubble-card-editor")}getLayoutOptions(){let e=1;"pop-up"===this.config.card_type?e=0:"horizontal-buttons-stack"===this.config.card_type?e=1:["cover"].includes(this.config.card_type)&&(e=2);let t=4;return"pop-up"===this.config.card_type?t=0:"horizontal-buttons-stack"===this.config.card_type&&(t=4),{grid_columns:this.config.columns??t,grid_rows:this.config.rows??e}}}customElements.define("bubble-card",ue),window.customCards=window.customCards||[],window.customCards.push({type:"bubble-card",name:"Bubble Card",preview:!1,description:"A minimalist card collection with a nice pop-up touch.",documentationURL:"https://github.com/Clooos/Bubble-Card/"}),console.info(`%c Bubble Card %c ${e} `,"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)")})()})();