mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Issue: #679 Issue: #680 Issue: #681 Issue: #682 Issue: #683 User-Visible: yes Release: v1.78.0-beta.7
258 lines
53 KiB
JavaScript
258 lines
53 KiB
JavaScript
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="dd80d3b0c884adfebcd12a9269ff41ef8ed9ad0f357a8b71dc65cf442b0eb5a2";import{A as e,c as t,s as a,l as i,d as o,ap as n,h as s,j as l,g as r,dU as c,dV as p,dW as d,c_ as h,dX as u,cZ as m,dY as g,d3 as _,d2 as b,dZ as f,bK as v,bJ as w,bL as $,av as y,aC as k,aD as x,d_ as C}from"./houseplan-card-C7lWkj4E.js";import{c as S,f as T,C as M}from"./form-kit.styles-BiAlo9qY.js";import{n as P}from"./namespace-language-DfhvcdpP.js";const L="#1f2a30";function E(e,t){const a=function(e){const t=/^#?([0-9a-f]{6})$/i.exec(e.trim());if(!t)return null;const a=parseInt(t[1],16);return[a>>16&255,a>>8&255,255&a]}(e);if(!a)return L;const i=Number.isFinite(t)?Math.min(1,Math.max(0,t)):1;return function([e,t,a]){return(.299*e+.587*t+.114*a)/255>.6}(a.map(e=>e*i+211*(1-i)))?L:"#fff"}const D=S+T(M,{withSwitch:!1}),F=new WeakSet;let H=null;function I(e){const t=e.renderRoot;if(!t||F.has(t))return;const a=t.ownerDocument??globalThis.document,i=a?.defaultView?.CSSStyleSheet;if(i&&"adoptedStyleSheets"in t)return H||(H=new i,H.replaceSync(D)),t.adoptedStyleSheets=[...t.adoptedStyleSheets,H],void F.add(t);if(t.querySelector("style[data-hp-form-kit]"))return void F.add(t);const o=a.createElement("style");o.dataset.hpFormKit="true",o.textContent=D,t.appendChild(o),F.add(t)}function R({title:a,help:i,body:o,id:n}){return t`<section class="hpf-card" data-card=${n??e}>
|
||
${a?t`<div class="hpf-head"><h3>${a}</h3>${i??e}</div>`:e}
|
||
<div class="hpf-body">${o}</div>
|
||
</section>`}function N({title:a,help:i}){return t`<div class="hpf-sub"><h4>${a}</h4>${i??e}</div>`}function O({label:a,htmlFor:i,help:o,control:n,hint:s,error:l}){return t`<div class="hpf-field">
|
||
<span class="hpf-label hpf-labelrow">${i?t`<label for=${i}>${a}</label>`:a}${o??e}</span>
|
||
${n}
|
||
${l?t`<p class="hpf-error" role="alert">${l}</p>`:s?t`<p class="hpf-hint">${s}</p>`:e}
|
||
</div>`}function B(e,{narrow:a=!1}={}){return t`<div class="hpf-grid ${a?"hpf-grid-narrow":""}">${e}</div>`}function z({icon:a,title:i,caption:o,help:n,checked:s,disabled:l=!1,compact:r=!1,onChange:c,id:p}){const d=p&&o?`${p}-caption`:void 0;return t`<label class="hpf-toggle ${a?"":"hpf-noicon"} ${r?"hpf-compact":""}">
|
||
${a?t`<span class="hpf-toggle-icon" aria-hidden="true"><ha-icon icon=${a}></ha-icon></span>`:e}
|
||
<span class="hpf-toggle-title"><span>${i}</span>${n??e}</span>
|
||
${o&&!r?t`<span class="hpf-toggle-caption" id=${d??e}>${o}</span>`:e}
|
||
<input type="checkbox" id=${p??e} .checked=${s} ?disabled=${l}
|
||
aria-label=${i} aria-describedby=${d??e}
|
||
@change=${e=>c(e.target.checked)} />
|
||
</label>`}function q({name:a,value:i,options:o,onChange:n,ariaLabel:s,disabled:l=!1}){return t`<div class="hpf-seg" role="radiogroup" aria-label=${s??e}
|
||
aria-disabled=${l?"true":e}>
|
||
${o.map(o=>t`<label>
|
||
<input type="radio" name=${a} value=${o.value} .checked=${o.value===i}
|
||
?disabled=${l||!!o.disabled} aria-describedby=${o.describedBy??e}
|
||
@change=${()=>n(o.value)} />
|
||
${o.sample?t`<span class="hpf-line ${"dashed"===o.sample?"hpf-line-dashed":""}" aria-hidden="true"></span>`:e}
|
||
${o.icon?t`<ha-icon icon=${o.icon} aria-hidden="true"></ha-icon>`:e}
|
||
<span>${o.label}</span>
|
||
</label>`)}
|
||
</div>`}function A(a,i){return t`<div class="hpf-tiles" role="group" aria-label=${i??e}>
|
||
${a.map(a=>t`<label class="hpf-tile" title=${a.title??e}>
|
||
<input type="checkbox" id=${a.id??e} .checked=${a.checked} ?disabled=${a.disabled}
|
||
aria-label=${a.title??a.label}
|
||
@change=${e=>a.onChange(e.target.checked)} />
|
||
<ha-icon icon=${a.icon} aria-hidden="true"></ha-icon>
|
||
<span>${a.label}</span>
|
||
</label>`)}
|
||
</div>`}function U({picker:a,hex:i,opacity:o,opacityLabel:n,onOpacity:s,resetLabel:l,onReset:r,disabled:c=!1}){const p=void 0===o?null:Math.round(100*Math.min(1,Math.max(0,o)));return t`<div class="hpf-colorfield">
|
||
${a}
|
||
<code class="hpf-hex">${i.toUpperCase()}</code>
|
||
${null===p?e:t`<span class="hpf-opacity">${n??""}
|
||
<span class="hpf-unit"><input type="number" inputmode="numeric" min="0" max="100" step="1"
|
||
.value=${String(p)} ?disabled=${c} aria-label=${n??"Opacity"}
|
||
@input=${e=>{const t=Number(e.target.value);Number.isFinite(t)&&s&&s(Math.min(1,Math.max(0,t/100)))}} /><span>%</span></span></span>`}
|
||
${l&&r?t`<button class="hpf-link" type="button" ?disabled=${c} @click=${r}>${l}</button>`:e}
|
||
</div>`}function j({label:e,hex:a,opacity:i,opacityLabel:o,picker:n,onOpacity:s}){const l=Math.round(100*Math.min(1,Math.max(0,i)));return t`<div class="hpf-colortile">
|
||
<div class="hpf-colortile-swatch" style=${`color:${E(a,i)}`}>
|
||
${n}<span>${e}</span>
|
||
</div>
|
||
<div class="hpf-colortile-meta">
|
||
<code class="hpf-hex">${a.toUpperCase()}</code>
|
||
<span class="hpf-unit" title=${o}><input type="number" inputmode="numeric" min="0" max="100" step="1"
|
||
.value=${String(l)} aria-label=${o}
|
||
@input=${e=>{const t=Number(e.target.value);Number.isFinite(t)&&s(Math.min(1,Math.max(0,t/100)))}} /><span>%</span></span>
|
||
</div>
|
||
</div>`}function W(e){return t`<div class="hpf-colortiles">${e}</div>`}function V({label:a,help:i,picker:o,action:n}){return t`<div class="hpf-colorrow">
|
||
<span class="hpf-labelrow">${a}${i??e}</span>
|
||
${o}${n??e}
|
||
</div>`}function G({label:a,help:i,name:o,value:n,options:s,onChange:l}){return t`<div class="hpf-inline hpf-wrap">
|
||
<span class="hpf-labelrow">${a}${i??e}</span>
|
||
<div class="hpf-radios" role="radiogroup" aria-label=${a}>
|
||
${s.map(e=>t`<label><input type="radio" name=${o} .checked=${e.value===n}
|
||
@change=${()=>l(e.value)} /><span>${e.label}</span></label>`)}
|
||
</div>
|
||
</div>`}function Y({text:a,kind:i="info",icon:o,action:n,role:s,id:l}){return t`<div id=${l??e} class="hpf-callout ${"warning"===i?"hpf-warning":""}" role=${s??e}>
|
||
<ha-icon icon=${o??("warning"===i?"mdi:alert-outline":"mdi:information-outline")}></ha-icon>
|
||
<p>${a}${n?t` ${n}`:e}</p>
|
||
</div>`}function Z({id:a,describedBy:i,value:o,unit:n,min:s,max:l,step:r,placeholder:c,ariaLabel:p,invalid:d,disabled:h,wide:u,onInput:m,onChange:g}){return t`<span class="hpf-unit ${u?"hpf-unit-wide":""}">
|
||
<input id=${a??e} type="number" inputmode="decimal" .value=${o}
|
||
aria-describedby=${i??e}
|
||
min=${s??e} max=${l??e} step=${r??e}
|
||
placeholder=${c??e} aria-label=${p??e}
|
||
aria-invalid=${d?"true":e} ?disabled=${h}
|
||
@input=${e=>m(e.target.value)}
|
||
@change=${g?e=>g(e.target.value,e.target):e} />
|
||
<span>${n}</span>
|
||
</span>`}function K(e,t,a,i){if(!e.trim())return null;const o=Number(e);if(!Number.isFinite(o)||!Number.isFinite(i)||i<=0)return null;const n=Math.min(a,Math.max(t,o)),s=t+Math.round((n-t)/i)*i,l=function(e){const t=String(e).toLowerCase();if(!t.includes("e"))return(t.split(".")[1]||"").length;const[a,i]=t.split("e"),o=(a.split(".")[1]||"").length;return Math.max(0,o-Number(i))}(i);return Math.min(a,Math.max(t,Number(s.toFixed(l))))}function Q({min:e,max:a,step:i,value:o,unit:n,ariaLabel:s,disabled:l,slider:r,onInput:c,id:p}){return t`<div class="hpf-range">
|
||
${r}
|
||
${Z({id:p,value:String(o),unit:n,min:e,max:a,step:i,ariaLabel:s,disabled:l,onInput:()=>{},onChange:(t,n)=>{const s=K(t,e,a,i);null!==s?(n.value=String(s),c(s)):n.value=String(o)}})}
|
||
</div>`}function J(e,a){return t`<div class="hpf-range-ends" aria-hidden="true">
|
||
<span class="hpf-range-ends-track"><span>${e}</span><span>${a}</span></span>
|
||
</div>`}function X(e,a,{disabled:i=!1}={}){return t`<button class="hpf-link" type="button" ?disabled=${i} @click=${a}>${e}</button>`}function ee({text:a,action:i}){return t`<span class="hpf-status" role="status" aria-live="polite">${a??""}${i??e}</span>`}function te({title:a,tag:i,body:o}){return t`<div class="hpf-tint">
|
||
<div class="hpf-tint-head"><h4>${a}</h4>${i?t`<span class="hpf-tag">${i}</span>`:e}</div>
|
||
${o}
|
||
</div>`}function ae({open:a,current:i,placeholder:o,ariaLabel:n,filter:s,filterPlaceholder:l,candidates:r,selected:c,emptyText:p,onToggle:d,onFilter:h,onPick:u,id:m,toolbar:g,disabled:_=!1}){return t`<div class="hpf-picker ${a?"open":""}">
|
||
<button id=${m??e} type="button" class="hpf-select hpf-pick" aria-haspopup="listbox"
|
||
aria-expanded=${a?"true":"false"} aria-label=${n} ?disabled=${_} @click=${d}>
|
||
${i?t`<b>${i.label}</b><span class="hpf-pick-ref">${i.sub}</span>`:t`<span class="hpf-pick-ph">${o}</span>`}
|
||
</button>
|
||
${a?t`<div class="hpf-panel">
|
||
<div class="hpf-toolbar">
|
||
<input class="hpf-input" type="text" autocomplete="off" placeholder=${l}
|
||
aria-label=${l} .value=${s}
|
||
@input=${e=>h(e.target.value)} />
|
||
${g??e}
|
||
</div>
|
||
<div class="hpf-list" role="listbox" aria-label=${n}>
|
||
${r.length?r.map(e=>t`<button type="button" role="option" class="hpf-cand ${e.value===c?"sel":""}"
|
||
aria-selected=${e.value===c?"true":"false"} @click=${()=>u(e.value)}>
|
||
<span>${e.label}</span><small>${e.sub}</small>
|
||
</button>`):t`<p class="hpf-hint">${p}</p>`}
|
||
</div>
|
||
</div>`:e}
|
||
</div>`}var ie={"space.scale_changed":"Changing the scale recalculates the dimensions of the whole space when you save. Points on the plan stay where they are.","space.plan.help":"Use an image as a background to draw over. Rooms and devices stay editable on top of it. Switching to drawing by hand keeps the uploaded image available to use again.","space.plan.help.aria":"Help: floor plan","space.source_draw_hint":"Outline rooms in the plan editor.","space.source_file_hint":"Add an image to draw over.","space.plan_kept":"Your uploaded image is kept. Switch back to use it again.","space.show_borders_hint":"Show patterned outlines around the walls.","space.show_borders.help":"When off, patterned wall outlines and zero-thickness wall lines are hidden. Plain, light wall bands stay visible, so rooms remain separated. Doors, windows, room fills and lighting keep working.","space.show_borders.help.aria":"Help: room borders","space.layers_label":"Visible layers","space.layers.help":"Hiding changes only what you see on the plan. Decorative elements remain available in the background editor. Doors and windows stay in the plan editor, while their sensors, sunlight and lamp glow keep working.","space.layers.help.aria":"Help: visible layers","space.layer_decor":"Decorative layer","space.layer_openings":"Doors, windows & gates","space.fill_lqi_hint":"Uses average room LQI: low at 40 or below, high at 180 or above. Colors and opacity follow the general palette.","space.fill_light_hint":"Uses separate colors for any light on, all lights off, and no light sources. Colors and opacity follow the general palette.","space.temp_range":"Comfort range","space.temp_min":"Minimum","space.temp_max":"Maximum","space.temp_range_hint":"Below the minimum: cold. Within the range: comfort. Above the maximum: hot. Without a temperature reading, light-source glow can still add base shading.","space.show_names_hint":"Display room names and their selected values.","space.show_names.help":"Hiding room names also hides their values without clearing your selections. Room-card positions stay where you placed them.","space.show_names.help.aria":"Help: room names","space.card_values":"Values on the card","space.label_lqi_short":"Zigbee signal","space.values_hidden":"Values are saved but hidden.","space.values_hidden_action":"Show room names","space.card_font_hint":"Scales every room card in this space.","space.card_font.help":"100% is the default size. This scales every room card in this space while keeping individual card sizes and positions. Changes apply when you save.","space.card_font.help.aria":"Help: room-card font size","space.sun_inherit_short":"Use general settings","space.north_general":"Use general settings · {v}","space.north_custom":"Custom direction","space.north_deg":"North direction","space.compass_aria":"Compass: north at {v}","space.glow_enabled_hint":"Show a soft glow around lights that are on.","space.glow_enabled.help":"Lamp glow and sunlight are independent. Hiding opening symbols does not stop either effect. Window glass still lets sunlight in when the window is closed.","space.glow_enabled.help.aria":"Help: light-source glow","space.error_title":"Enter a name for the space.","space.error_plan":"Add a floor-plan image or switch to drawing by hand.","space.error_scale":"Enter a scale within the allowed range.","space.error_temp_value":"Enter a number.","space.error_temp_range":"The maximum must be higher than the minimum.","space.error_north":"Enter a whole number between 0° and 359°.","btn.reset_100":"Reset to 100%","dialog.review_fields":"Review {n} field(s)","dialog.discard_title":"Discard your changes?","dialog.discard_message":"Your changes haven’t been saved.","dialog.discard_confirm":"Discard","dialog.discard_keep":"Continue","gs.show_room_tooltip_hint":"Hover a room on the plan to see its summary.","gs.radar_show_live_hint":"Hides or shows all live radar targets. This does not start or stop observation recording.","gs.volumetric_view_hint":"Renders walls, doors, windows and device markers with depth. Decor and the flat plan stay unchanged.","gs.bg_custom_hint":"Custom background color.","gs.bg_theme_hint":"Using the theme background.","gs.sun_rays_hint":"Draw sun rays through window openings.","gs.error_glow_radius":"Enter a radius greater than zero.","gs.error_north":"Enter a whole number between 0° and 359°.","gs.opacity_of":"{name} opacity","room.fill_inherit_hint":"Use the fill mode set for the whole space.","room.fill_following":"Following the space: {mode}.","room.fill_mode_label":"Fill mode","room.temp_min_label":"Minimum","room.temp_max_label":"Maximum","room.sizes_hint":"On top of the space-wide card size ({v}%).","room.error_temp_range":"Enter numbers for the comfort bounds.","room.error_name":"Enter a name or choose an area.","marker.binding.help":"A bound marker shows the live state of a Home Assistant device or entity and can act on it. A virtual device is a plain marker with no binding.","marker.binding.help.aria":"Help: binding to a Home Assistant device","marker.binding_virtual_hint":"A virtual device is a plain marker with no binding.","marker.room_hint_auto":"Placed by the device’s Home Assistant area.","marker.room_hint_override":"Overrides the placement by device area.","marker.light_role_seg_auto":"Auto","marker.light_role_seg_always":"Always","marker.light_role_seg_never":"Never","marker.glow_radius_empty_hint":"Empty: uses the general radius ({v}).","marker.icon.help":"Empty means automatic: the icon comes from the icon rules for this device. Pick one to override, or pin the automatic choice.","marker.icon.help.aria":"Help: marker icon","marker.icon_custom_hint":"Custom icon for this marker.","marker.display.help":"How the marker face reacts to state and activity. The value modes replace the icon with a Home Assistant value.","marker.display.help.aria":"Help: display mode","marker.size.help":"Scales and rotates only this marker on the plan. The tap area grows with the icon.","marker.size.help.aria":"Help: icon size and rotation","marker.size_short":"Size","marker.error_binding":"Choose a device or entity to bind.","marker.error_virtual_name":"Enter a name for the virtual device.","marker.error_run_target":"Choose what to run.","marker.error_value_badge_source":"Choose a value source for the badge.","fill.seg_none":"None","fill.seg_lqi":"Zigbee","fill.seg_light":"Lights","fill.seg_temp":"Temperature","fill.seg_custom":"Custom"};const oe=e=>new URL(`${e}?retry`,import.meta.url).href,ne=P(ie,{ru:e=>0===e?import("./settings-ru-CoCWtF-R.js"):import(oe("./settings-ru-CoCWtF-R.js")),de:e=>0===e?import("./settings-de-CG0vqBCP.js"):import(oe("./settings-de-CG0vqBCP.js")),fr:e=>0===e?import("./settings-fr-Dlnfr7CE.js"):import(oe("./settings-fr-Dlnfr7CE.js"))}),se=(e,t)=>ne.dictionary(e)?.[t]??ie[t];function le(e,t){const a=se(e,t);return"string"==typeof a&&a.trim().length>0}function re(e,t,i){return a(se(e,t)??t,i)}function ce(a){const o=i(a.hass,a._config?.language);return{st:(e,t)=>re(o,e,t),shelp:a=>{const i=`${a}.aria`;return le(o,a)&&le(o,i)?t`<hp-help data-help-key=${a} .text=${re(o,a)} .ariaLabel=${re(o,i)}></hp-help>`:e}}}const pe=new Set(["busy","pickSaved","saved","savedBusy","savedAspect","cellCmInput","tempMinInput","tempMaxInput","northDegInput","cellCmTouched","displayTouched","deleteBlockers","copy"]);function de(e){return s({...e,planFile:e.planFile?e.planFile.name:null},pe)}function he(e,t){o(e,"space",de(t))}function ue(e,t,a=!1){const i=[];e.title.trim()||i.push({field:`${t}-title`,message:"space.error_title"});const o=e.cellCmInput??String(e.cellCm),s=n(o),l=null===s?null:d(s,a);if((null===l||l<c||l>p)&&i.push({field:`${t}-cell-cm`,message:"space.error_scale"}),"file"!==e.source||e.planFile||e.planUrl||i.push({field:`${t}-plan`,message:"space.error_plan"}),"temp"===e.fillMode){const a=n(e.tempMinInput??String(e.tempMin)),o=n(e.tempMaxInput??String(e.tempMax));null===a&&i.push({field:`${t}-temp-min`,message:"space.error_temp_value"}),null===o&&i.push({field:`${t}-temp-max`,message:"space.error_temp_value"}),null!==a&&null!==o&&a>=o&&i.push({field:`${t}-temp-max`,message:"space.error_temp_range"})}if(null!==e.northDeg){const a=n(e.northDegInput??String(e.northDeg));null!==a&&Number.isInteger(a)&&a>=0&&a<=359||i.push({field:`${t}-north-deg`,message:"space.error_north"})}return i}const me=c,ge=p,_e=(e,t)=>{e.host._spaceDialog=t};function be(a){I(a.host);const{host:i}=a,o=i._spaceDialog,n=i._t.bind(i),{st:s}=ce(i),c=e=>`${a.idPrefix}-${e}`,p=ue(o,a.idPrefix,i._imperial),d=e=>p.find(t=>t.field===e),h=function(e,t){return l(e,"space",de(t))}(i,o),u=h&&0===p.length&&!o.busy,m=i._importTotal>0&&"create"===o.mode?n("import.progress",{i:i._importTotal-i._importQueue.length,n:i._importTotal}):"",g=()=>{!function(e){r(e,"space")}(i),i._spaceDialog=null,i._importQueue=[],i._importTotal=0},_=async e=>{if(!h||o.busy)return void g();const t=e.currentTarget;await i._confirmDanger({key:"discard-space-dialog",kind:"warning",title:s("dialog.discard_title"),message:s("dialog.discard_message"),objectName:o.title.trim()||void 0,confirmLabel:s("dialog.discard_confirm"),cancelLabel:s("dialog.discard_keep"),icon:"mdi:content-save-off-outline",confirmIcon:"mdi:content-save-off-outline"})?g():t?.rejectClose?.()};return{title:`${"create"===o.mode?n("space.new"):n("space.header")}${m?` · ${m}`:""}`,badge:"edit"===o.mode?o.title.trim():"",requestClose:_,body:t`<div class="body hpf-form">
|
||
${fe(a,o,c,d)}
|
||
${we(a,o,c,d)}
|
||
${$e(a,o,c)}
|
||
${ye(a,o,c,d)}
|
||
${o.deleteBlockers?Y({kind:"warning",role:"alert",text:n("space.delete_blocked",{n:String(o.deleteBlockers)})}):e}
|
||
</div>`,footer:t`<div class="row dialog-action-footer hpf-footer hpf-footer-space" slot="footer">
|
||
${"edit"===o.mode&&a.copySpace?t`<div class="dialog-action-group dialog-action-copy">
|
||
<button class="btn ghost hpf-mobile-icon" aria-label=${n("btn.copy")} title=${n("btn.copy")}
|
||
@click=${()=>a.copySpace()} ?disabled=${o.busy}>
|
||
<ha-icon icon="mdi:content-copy"></ha-icon><span class="hpf-action-label">${n("btn.copy")}</span>
|
||
</button>
|
||
</div>`:e}
|
||
${"edit"===o.mode&&a.deleteSpace?t`<div class="dialog-action-group dialog-action-danger">
|
||
<button class="btn danger hpf-mobile-icon" aria-label=${n("btn.delete")} title=${n("btn.delete")}
|
||
@click=${()=>a.deleteSpace()} ?disabled=${o.busy}>
|
||
<ha-icon icon="mdi:delete-outline"></ha-icon><span class="hpf-action-label">${n("btn.delete")}</span>
|
||
</button>
|
||
</div>`:e}
|
||
${ee(p.length?{action:X(s("dialog.review_fields",{n:String(p.length)}),()=>{const e=p[0];if(!e)return;const t=i.renderRoot.querySelector(`#${e.field}`);t?.scrollIntoView({block:"center"}),t?.focus()})}:{})}
|
||
<div class="dialog-action-group dialog-action-commit">
|
||
${i._importTotal>0&&"create"===o.mode?t`<button class="btn ghost" @click=${()=>a.skipImport()}>${n("btn.skip")}</button>`:e}
|
||
<button class="btn ghost" data-hp="dialog-cancel" @click=${_}>${n("btn.cancel")}</button>
|
||
<button class="btn on" data-hp="dialog-confirm" @click=${()=>a.save()} ?disabled=${!u}
|
||
title=${p[0]?s(p[0].message):""}>
|
||
<ha-icon icon="mdi:check"></ha-icon>${o.busy?"…":n("btn.save")}
|
||
</button>
|
||
</div>
|
||
</div>`}}function fe(a,i,o,s){const{host:l}=a,r=l._t.bind(l),{st:c,shelp:p}=ce(l),m="edit"===i.mode&&i.spaceId?l._serverCfg?.spaces.find(e=>e.id===i.spaceId):void 0,g=m&&Number(m.cell_cm)>0?Number(m.cell_cm):null,_=null!==g&&Math.abs(i.cellCm-g)>1e-9,b=s(o("title")),f=s(o("cell-cm")),v=s(o("plan"));return R({id:"basics",title:r("space.card_basics"),body:t`
|
||
${B([O({label:r("space.title_label"),htmlFor:o("title"),error:b?c(b.message):void 0,control:t`<input id=${o("title")} class="hpf-input" type="text" maxlength="80"
|
||
placeholder=${r("space.title_ph")} .value=${i.title}
|
||
aria-invalid=${b?"true":e}
|
||
@input=${e=>_e(a,{...i,title:e.target.value})} />`}),O({label:r("space.scale_label"),htmlFor:o("cell-cm"),help:a.help("space.cell_cm.help"),error:f?c(f.message):void 0,control:Z({id:o("cell-cm"),wide:!0,value:i.cellCmInput??h(i.cellCm,l._imperial),unit:r(l._imperial?"space.scale_unit_imperial":"space.scale_unit"),min:Number(h(me,l._imperial)),max:Number(h(ge,l._imperial)),step:.1,invalid:!!f,onInput:e=>{const t=n(e),o=null==t?null:d(t,l._imperial);_e(a,{...i,cellCmInput:e,cellCmTouched:!0,cellCm:null!=o&&o>=me&&o<=ge?o:i.cellCm})}})})],{narrow:!0})}
|
||
${_?Y({kind:"warning",text:c("space.scale_changed")}):e}
|
||
${N({title:r("space.plan_label"),help:p("space.plan.help")})}
|
||
<div id=${o("plan")} tabindex="-1" aria-invalid=${v?"true":e}>
|
||
${w="plansrc",$=i.source,y=[{value:"draw",title:r("space.source_draw"),caption:c("space.source_draw_hint")},{value:"file",title:r("space.source_file"),caption:c("space.source_file_hint")}],k=e=>_e(a,u(i,e)),x=r("space.plan_label"),t`<div class="hpf-choices" role="radiogroup" aria-label=${x??e}>
|
||
${y.map(e=>t`<label class="hpf-choice">
|
||
<input type="radio" name=${w} .checked=${e.value===$} @change=${()=>k(e.value)} />
|
||
<strong>${e.title}</strong><small>${e.caption}</small>
|
||
</label>`)}
|
||
</div>`}
|
||
</div>
|
||
${"file"===i.source?ve(a,i):e}
|
||
${"draw"===i.source&&i.planUrl?Y({text:c("space.plan_kept")}):e}
|
||
${v?t`<p class="hpf-error" role="alert">${c(v.message)}</p>`:e}`});var w,$,y,k,x}function ve(a,i){const{host:o}=a,n=o._t.bind(o);return t`<div class="planrow">
|
||
${i.planFile?t`<span class="planname">${i.planFile.name}</span>`:i.planUrl?t`<img class="planprev" src=${o._display(i.planUrl)} alt=${n("space.plan_alt")} />`:t`<span class="planname muted">${n("space.no_plan")}</span>`}
|
||
<span class="fileupload">
|
||
<button class="btn filebtn" type="button" @click=${e=>e.currentTarget.nextElementSibling?.click()}>
|
||
<ha-icon icon="mdi:upload"></ha-icon>${i.planUrl||i.planFile?n("btn.replace"):n("btn.upload")}
|
||
</button>
|
||
<input type="file" hidden accept=".svg,.png,.jpg,.jpeg,.webp,image/svg+xml,image/png,image/jpeg,image/webp"
|
||
@change=${e=>a.pickPlanFile(e)} />
|
||
</span>
|
||
<button class="btn ghost" type="button" @click=${()=>a.toggleServerPlans()}
|
||
title=${n("space.pick_saved_hint")}>
|
||
<ha-icon icon="mdi:folder-image"></ha-icon>${n("space.pick_saved")}
|
||
</button>
|
||
</div>
|
||
${i.pickSaved?a.renderServerPlans(i):e}`}function we(e,a,i,o){const{host:s}=e,l=s._t.bind(s),{st:r,shelp:c}=ce(s),p=o(i("temp-min")),d=o(i("temp-max")),h="custom"===a.fillMode?V({label:l("space.custom_fill"),picker:U({hex:(a.customFill||m).c,opacity:(a.customFill||m).a,opacityLabel:l("space.opacity"),onOpacity:t=>_e(e,{...a,customFill:{c:(a.customFill||m).c,a:t}}),resetLabel:a.customFill?l("btn.reset"):void 0,onReset:()=>_e(e,{...a,customFill:null}),picker:t`<hp-color-opacity .label=${l("space.custom_fill")} hide-label flat-swatch .opacityLabel=${l("space.opacity")}
|
||
.pickerLabels=${s._colorPickerLabels}
|
||
.color=${(a.customFill||m).c} .opacity=${(a.customFill||m).a}
|
||
@hp-color-opacity-change=${t=>{_e(e,{...a,customFill:{c:t.detail.color,a:t.detail.opacity}})}}></hp-color-opacity>`})}):"temp"===a.fillMode?t`<div class="hpf-field">
|
||
<span class="hpf-label hpf-labelrow">${r("space.temp_range")}</span>
|
||
${B([O({label:r("space.temp_min"),htmlFor:i("temp-min"),error:p?r(p.message):void 0,control:Z({id:i("temp-min"),wide:!0,value:a.tempMinInput??String(a.tempMin),unit:"°C",step:.5,invalid:!!p,onInput:t=>{const i=n(t);_e(e,{...a,tempMinInput:t,...null!==i?{tempMin:i}:{}})}})}),O({label:r("space.temp_max"),htmlFor:i("temp-max"),error:d?r(d.message):void 0,control:Z({id:i("temp-max"),wide:!0,value:a.tempMaxInput??String(a.tempMax),unit:"°C",step:.5,invalid:!!d,onInput:t=>{const i=n(t);_e(e,{...a,tempMaxInput:t,...null!==i?{tempMax:i}:{}})}})})])}
|
||
<div class="hpf-templegend" aria-hidden="true">
|
||
<span>${l("gs.temp_cold")}</span><span>${l("gs.temp_ok")}</span><span>${l("gs.temp_hot")}</span>
|
||
</div>
|
||
<p class="hpf-hint">${r("space.temp_range_hint")}</p>
|
||
</div>`:t`<p class="hpf-hint">${r("lqi"===a.fillMode?"space.fill_lqi_hint":"space.fill_light_hint")}</p>`;return R({id:"appearance",title:l("space.display_section"),body:t`
|
||
${z({id:i("show-borders"),icon:"mdi:border-none-variant",title:l("space.show_borders"),caption:r("space.show_borders_hint"),help:c("space.show_borders.help"),checked:a.showBorders,onChange:t=>_e(e,g(a,"showBorders",t))})}
|
||
${O({label:l("space.zero_wall_style"),help:e.help("space.zero_wall_style.help"),control:q({name:`${e.idPrefix}-zero-wall-style`,value:"solid"===a.zeroWallStyle?"solid":"dashed",ariaLabel:l("space.zero_wall_style"),options:[{value:"dashed",label:l("space.zero_wall_dashed"),sample:"dashed"},{value:"solid",label:l("space.zero_wall_solid"),sample:"solid"}],onChange:t=>_e(e,{...a,zeroWallStyle:"solid"===t?"solid":"dashed"})})})}
|
||
${V({label:l("space.room_color"),picker:U({hex:a.roomColor,opacity:a.roomOpacity,opacityLabel:l("space.opacity"),onOpacity:t=>_e(e,{...a,roomOpacity:t}),resetLabel:a.roomColor!==_||a.roomOpacity!==b?l("btn.reset"):void 0,onReset:()=>_e(e,{...a,roomColor:_,roomOpacity:b}),picker:t`<hp-color-opacity .label=${l("space.room_color")} hide-label flat-swatch .opacityLabel=${l("space.opacity")}
|
||
.pickerLabels=${s._colorPickerLabels}
|
||
.color=${a.roomColor} .opacity=${a.roomOpacity} .showOpacity=${!0}
|
||
@hp-color-opacity-change=${t=>{_e(e,{...a,roomColor:t.detail.color,roomOpacity:t.detail.opacity})}}></hp-color-opacity>`})})}
|
||
${N({title:l("space.fill_label"),help:e.help("space.fill_mode.help")})}
|
||
${q({name:`${e.idPrefix}-fill-mode`,value:"none"===a.fillMode?"custom":a.fillMode,ariaLabel:l("space.fill_label"),options:f.map(e=>({value:e,label:r(`fill.seg_${e}`)})),onChange:t=>_e(e,{...a,fillMode:t})})}
|
||
${h}
|
||
${N({title:r("space.layers_label"),help:c("space.layers.help")})}
|
||
${u=[z({id:i("layer-decor"),compact:!0,icon:"mdi:sofa-outline",title:r("space.layer_decor"),checked:!a.hideDecor,onChange:t=>_e(e,{...a,hideDecor:!t})}),z({id:i("layer-openings"),compact:!0,icon:"mdi:door",title:r("space.layer_openings"),checked:!a.hideOpenings,onChange:t=>_e(e,{...a,hideOpenings:!t})}),z({id:i("show-lqi"),compact:!0,icon:"mdi:zigbee",title:l("space.show_lqi"),checked:a.showLqi,onChange:t=>_e(e,{...a,showLqi:t})})],t`<div class="hpf-compact-list">${u}</div>`}`});var u}function $e(a,i,o){const{host:n}=a,s=n._t.bind(n),{st:l,shelp:r}=ce(n),c=Math.round(100*i.cardFontScale),p=[["labelTemp","space.label_temp","mdi:thermometer",void 0],["labelHum","space.label_hum","mdi:water-outline",void 0],["labelLqi","space.label_lqi_short","mdi:zigbee","space.label_lqi"],["labelLight","space.label_light","mdi:lightbulb-outline",void 0]].map(([e,t,n,r])=>({id:o(`tile-${e}`),label:"space.label_lqi_short"===t?l(t):s(t),icon:n,title:r?s(r):void 0,checked:i[e],disabled:!i.showNames,onChange:t=>_e(a,{...i,[e]:t})}));return R({id:"room-cards",title:s("space.roomcard_section"),body:t`
|
||
${z({id:o("show-names"),icon:"mdi:format-text",title:s("space.show_names"),caption:l("space.show_names_hint"),help:r("space.show_names.help"),checked:i.showNames,onChange:e=>_e(a,g(i,"showNames",e))})}
|
||
<div class="hpf-field">
|
||
<span class="hpf-label hpf-labelrow">${l("space.card_values")}</span>
|
||
${A(p,l("space.card_values"))}
|
||
${i.showNames?e:Y({icon:"mdi:eye-off-outline",text:l("space.values_hidden"),action:X(l("space.values_hidden_action"),()=>_e(a,g(i,"showNames",!0)))})}
|
||
</div>
|
||
<div class="hpf-field">
|
||
<div class="hpf-headline">
|
||
<span class="hpf-label hpf-labelrow"><label for=${o("card-font")}>${s("space.card_font")}</label>${r("space.card_font.help")}</span>
|
||
${X(l("btn.reset_100"),()=>_e(a,{...i,cardFontScale:1}),{disabled:100===c})}
|
||
</div>
|
||
${Q({id:o("card-font"),min:50,max:300,step:5,value:c,unit:"%",ariaLabel:s("space.card_font"),slider:a.rangeInput(50,300,5,c,e=>_e(a,{...i,cardFontScale:e/100}),!1,s("space.card_font")),onInput:e=>_e(a,{...i,cardFontScale:e/100})})}
|
||
${J("50%","300%")}
|
||
<p class="hpf-hint">${l("space.card_font_hint")}</p>
|
||
</div>
|
||
<div class="hpf-tint hpf-preview">${n._renderCardPreview(i.cardFontScale,1,1)}</div>`})}function ye(a,i,o,s){const{host:l}=a,r=l._t.bind(l),{st:c,shelp:p}=ce(l),d=v(l._settings,{}),h=i.northDeg??d,u=s(o("north-deg")),m=null===d?"—":`${d}°`;return R({id:"sun",title:r("space.card_sun"),body:t`
|
||
${O({label:r("space.bg_mode"),help:a.help("space.bg_mode.help"),control:q({name:`${a.idPrefix}-bg-mode`,value:i.bgMode??"",ariaLabel:r("space.bg_mode"),options:[{value:"",label:r("space.sun_inherit")},{value:"static",label:r("gs.bg_static")},{value:"daynight",label:r("gs.bg_daynight")}],onChange:e=>_e(a,{...i,bgMode:"static"===e||"daynight"===e?e:null})})})}
|
||
${"static"===(i.bgMode??w(l._settings,{}))?V({label:r("space.bg_color"),picker:U({hex:i.bgColor||$(l._settings,{bgColor:null})||l._stageBgHex(),resetLabel:i.bgColor?r("space.bg_inherit"):void 0,onReset:()=>_e(a,{...i,bgColor:null}),picker:t`<hp-color-opacity .label=${r("space.bg_color")} hide-label flat-swatch .pickerLabels=${l._colorPickerLabels}
|
||
.color=${i.bgColor||$(l._settings,{bgColor:null})||l._stageBgHex()}
|
||
.opacity=${1} .showOpacity=${!1}
|
||
@hp-color-opacity-change=${e=>_e(a,{...i,bgColor:e.detail.color})}></hp-color-opacity>`})}):e}
|
||
${G({label:r("space.sun_rays"),name:`${a.idPrefix}-sun-rays`,value:null===i.sunRays?"":i.sunRays?"1":"0",options:[{value:"",label:c("space.sun_inherit_short")},{value:"1",label:r("space.sun_on")},{value:"0",label:r("space.sun_off")}],onChange:e=>_e(a,{...i,sunRays:""===e?null:"1"===e})})}
|
||
${t`<hr class="hpf-divider" />`}
|
||
<div class="hpf-north">
|
||
<span class="hpf-labelrow"><label for=${o("north-mode")}>${r("space.north")}</label>${a.help("space.north.help")}</span>
|
||
<select id=${o("north-mode")} class="hpf-select"
|
||
@change=${e=>{const t="custom"===e.target.value?d??0:null;_e(a,{...i,northDeg:t,northDegInput:null===t?"":String(t)})}}>
|
||
<option value="inherit" ?selected=${null===i.northDeg}>${c("space.north_general",{v:m})}</option>
|
||
<option value="custom" ?selected=${null!==i.northDeg}>${c("space.north_custom")}</option>
|
||
</select>
|
||
<span class="hpf-north-n" aria-hidden="true">N</span>
|
||
${g=h,_=c("space.compass_aria",{v:null===h?"—":`${h}°`}),t`<span class="hpf-compass ${null===g?"hpf-unset":""}" role="img" aria-label=${_}>
|
||
<svg viewBox="0 0 24 24" style=${`transform: rotate(${null===g?0:(g%360+360)%360}deg)`} aria-hidden="true">
|
||
<path d="M12 3v18M12 3l-4 7h8z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />
|
||
</svg>
|
||
</span>`}
|
||
</div>
|
||
${null!==i.northDeg?O({label:c("space.north_deg"),htmlFor:o("north-deg"),error:u?c(u.message):void 0,control:t`<div class="hpf-inline hpf-wrap">
|
||
${Z({id:o("north-deg"),value:i.northDegInput??String(i.northDeg),unit:"°",min:0,max:359,step:1,invalid:!!u,onInput:e=>{const t=n(e);_e(a,{...i,northDegInput:e,...null!==t&&Number.isInteger(t)&&t>=0&&t<=359?{northDeg:t}:{}})}})}
|
||
${X(c("space.sun_inherit_short"),()=>_e(a,{...i,northDeg:null,northDegInput:""}))}
|
||
</div>`}):e}
|
||
${z({id:o("glow"),icon:"mdi:lightbulb-outline",title:r("space.glow_enabled"),caption:c("space.glow_enabled_hint"),help:p("space.glow_enabled.help"),checked:i.glowEnabled,onChange:e=>_e(a,{...i,glowEnabled:e})})}`});var g,_}function ke(e){const t=e.visualViewport;return t&&Number.isFinite(t.width)&&Number.isFinite(t.height)?{left:Number(t.offsetLeft)||0,top:Number(t.offsetTop)||0,width:Math.max(0,Number(t.width)||0),height:Math.max(0,Number(t.height)||0)}:{left:0,top:0,width:e.innerWidth,height:e.innerHeight}}function xe(e,t,a,i=7,o=8){const n=a.left+o,s=a.top+o,l=Math.max(n,a.left+a.width-o),r=Math.max(s,a.top+a.height-o),c=Math.max(0,l-n),p=Math.max(0,r-s),d=Math.min(Math.max(0,t.width),c),h=Math.min(Math.max(0,t.height),p);let u=e.left;u+d>l&&(u=e.right-d),u=Math.min(Math.max(n,u),Math.max(n,l-d));const m=e.bottom+i,g=e.top-i-h;let _="bottom",b=m;return m+h>r&&g>=s?(_="top",b=Math.min(Math.max(s,g),Math.max(s,r-h))):b=Math.min(Math.max(s,m),Math.max(s,r-h)),{left:Math.round(u),top:Math.round(b),side:_,maxWidth:Math.round(c),maxHeight:Math.round(p)}}class Ce{constructor(e,t,a){this.owner=e,this.name=t,this.keydown=a,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(e=!1){const t=this.window();return!e&&!!t&&function(e){const t=e.HTMLElement?.prototype;return"function"==typeof t?.showPopover&&"function"==typeof t?.hidePopover}(t)}get hasFallback(){return!!this.host}containsPath(e){return e.includes(this.owner)||!!this.host&&e.includes(this.host)}ownsActiveElement(){let e=this.owner.ownerDocument.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return!(!e||e!==this.owner&&!this.owner.shadowRoot?.contains(e)&&!this.root?.contains(e))}renderFallback(e,a){const i=this.ensureFallback();return i?(y(t`<style>${a}</style>${e}`,i),i):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&y(e,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const e=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!e)return null;const t=this.owner.ownerDocument.createElement("div");return t.dataset.hpOverlay=this.name,t.style.display="contents",this.keydown&&t.addEventListener("keydown",this.keydown,!0),e.append(t),this.host=t,this.root=t.attachShadow({mode:"open"}),this.root}}let Se=0;class Te extends x{constructor(){super(...arguments),this.text="",this.ariaLabel="",this.pointerHover=!1,this._open=!1,this._openTimer=0,this._closeTimer=0,this._positionRaf=0,this._forceFallback=!1,this._hoverOwned=!1,this._overlayDispose=null,this._scrollDialog=null,this._descriptionId="hp-help-description-"+ ++Se,this._surfacePointerEnter=()=>{const e=this._window();this._closeTimer&&e?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closeHelp(!1,"outside")},this._dialogScroll=e=>{const t=this._floating.containsPath(e.composedPath()),a=e.target,i=this._dialog(),o=a instanceof Node&&(a===i||a instanceof Element&&a.contains(this));var n;this._open&&(n=o,!t&&n)&&this._closeHelp(!1,"scroll")},this._keyDown=e=>{if(!this._open||e.defaultPrevented)return;if("Escape"===e.key)return e.preventDefault(),e.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const t=this.renderRoot.querySelector(".trigger");if(!t||!e.composedPath().includes(t))return;const a=this._surface();if(!a||a.scrollHeight<=a.clientHeight)return;const i=Math.max(40,.8*a.clientHeight),o={ArrowDown:40,ArrowUp:-40,PageDown:i,PageUp:-i};if("Home"===e.key)a.scrollTop=0;else if("End"===e.key)a.scrollTop=a.scrollHeight;else{if(!(e.key in o))return;a.scrollBy({top:o[e.key],behavior:"auto"})}e.preventDefault(),e.stopImmediatePropagation()},this._floating=new Ce(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const e=this._window();e&&(this._positionRaf=e.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const e=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&e?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(e){if(e.has("pointerHover")&&!this.pointerHover){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&e.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return e=this.text,t=this.ariaLabel,"string"==typeof e&&e.trim().length>0&&"string"==typeof t&&t.trim().length>0;var e,t}_clearTimers(){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._closeTimer&&e?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const e=this._window();this.pointerHover&&e&&!this._open&&!this._openTimer&&(this._closeTimer&&e.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=e.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const e=this._window(),t=this.renderRoot.querySelector(".trigger");e&&!t?.matches(":focus-visible")&&(this._openTimer&&e.clearTimeout(this._openTimer),this._closeTimer&&e.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=e.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(e){this.pointerHover&&"mouse"===e.pointerType&&this._scheduleOpen()}_triggerPointerLeave(e){"mouse"===e.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const e=this.renderRoot.querySelector(".trigger");e?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const e=this._window();e?.addEventListener("resize",this._queuePosition),e?.addEventListener("orientationchange",this._queuePosition),e?.visualViewport?.addEventListener("resize",this._queuePosition),e?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const e=this._window();e?.removeEventListener("resize",this._queuePosition),e?.removeEventListener("orientationchange",this._queuePosition),e?.visualViewport?.removeEventListener("resize",this._queuePosition),e?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:e=>this._closeHelp("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closeHelp()))}_closeHelp(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const a=this._overlayDispose;this._overlayDispose=null,a?.();const i=this.renderRoot.querySelector(".tooltip");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(a){return t`<div class="tooltip" data-side="bottom" popover=${a?"manual":e}
|
||
role="tooltip" aria-hidden="true" tabindex="-1"
|
||
@pointerenter=${this._surfacePointerEnter} @pointerleave=${this._surfacePointerLeave}>${this.text}</div>`}_renderFallback(){const e=Te.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),e)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const e=this._window(),t=this.renderRoot.querySelector(".trigger");let a=this._surface();if(!e||!t?.isConnected||!a?.isConnected)return!1;const i=ke(e);if(a.style.maxWidth=`${Math.max(0,Math.min(320,i.width-16))}px`,a.style.maxHeight=`${Math.max(0,i.height-16)}px`,a.style.visibility="hidden",this._usesPopover()&&a.showPopover)try{a.matches(":popover-open")||a.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),a=this._surface(),!a?.isConnected)return!1;a.style.visibility="hidden"}const o=t.getBoundingClientRect(),n=a.getBoundingClientRect();if(!(o.width&&o.height&&n.width&&n.height))return!1;const s=xe(o,n,i);return a.style.left=`${s.left}px`,a.style.top=`${s.top}px`,a.dataset.side=s.side,a.style.visibility="",!0}render(){if(!this._hasContent())return e;const a=this.ariaLabel.trim();return t`
|
||
<span id=${this._descriptionId} class="sr-only" role="tooltip"
|
||
aria-hidden=${this._open?"false":"true"}>${this.text}</span>
|
||
<button class="trigger" type="button" aria-label=${a}
|
||
aria-describedby=${this._open?this._descriptionId:e}
|
||
aria-expanded=${this._open?"true":"false"}
|
||
@pointerenter=${this._triggerPointerEnter} @pointerleave=${this._triggerPointerLeave}
|
||
@focus=${this._triggerFocus} @blur=${this._triggerBlur} @click=${this._triggerClick}>
|
||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||
<path d=${C}></path>
|
||
</svg>
|
||
</button>
|
||
${this._usesPopover()?this._tooltipTemplate(!0):e}
|
||
`}}Te.properties={text:{type:String},ariaLabel:{type:String,attribute:"aria-label"},pointerHover:{type:Boolean,attribute:"data-pointer-hover",reflect:!0},_open:{state:!0},_forceFallback:{state:!0}},Te.styles=k`:host{display:none;flex:none;vertical-align:middle;}:host([data-has-content]){display:inline-flex;}.trigger{width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;flex:none;box-sizing:border-box;padding:0;color:var(--secondary-text-color,#9aa4ad);background:transparent;border:0;border-radius:50%;cursor:help;-webkit-tap-highlight-color:transparent;}.trigger svg{display:block;width:18px;height:18px;fill:currentColor;}:host([data-pointer-hover]) .trigger:hover,.trigger:focus-visible,.trigger[aria-expanded='true']{color:var(--primary-text-color,#fff);background:rgb(127 127 127 / 0.18);outline:none;}.trigger:focus-visible{box-shadow:0 0 0 2px var(--primary-color,#03a9f4);}.tooltip{position:fixed;z-index:2147483647;inset:auto;top:0;left:0;width:max-content;max-width:min(320px,calc(100vw - 16px));max-height:calc(100vh - 16px);margin:0;box-sizing:border-box;padding:9px 11px;overflow:auto;color:var(--primary-text-color,#fff);background:var(--card-background-color,#202126);border:1px solid var(--divider-color,rgb(255 255 255 / 0.2));border-radius:9px;box-shadow:0 8px 24px rgb(0 0 0 / 0.32);font:400 13px/1.4 system-ui,sans-serif;text-align:start;white-space:normal;overflow-wrap:anywhere;opacity:1;transform:translateY(0);transition:opacity 120ms ease,transform 120ms ease;}.tooltip[data-side='top']{transform-origin:bottom center;}.tooltip[data-side='bottom']{transform-origin:top center;}.tooltip[popover]:not(:popover-open){display:none;}@starting-style{.tooltip{opacity:0;transform:translateY(3px);}}.sr-only{position:fixed;inset:0 auto auto 0;width:1px;height:1px;padding:0;margin:0;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0;}@media (pointer:coarse){.trigger{width:40px;height:40px;}}@media (prefers-reduced-motion:reduce){.tooltip{transition:none;}}`,customElements.get("hp-help")||customElements.define("hp-help",Te);function Me(e,t,a){const i=(e?.spaces||[]).find(e=>e?.id===a),o=new Set((i?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===a||"string"==typeof e.room_id&&o.has(e.room_id)||t?.[String(e.id)]?.s===a).map(e=>String(e.id)))].sort((e,t)=>e.localeCompare(t)),s=(e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>!n.includes(String(e.id))).filter(e=>((e,t)=>(e?.vacuum?.map_routes||[]).filter(e=>e&&e.space===t&&"string"==typeof e.id).map(e=>String(e.id)))(e,a).length>0).map(e=>String(e.id)).sort((e,t)=>e.localeCompare(t));return{markerIds:n,count:n.length,routeMarkerIds:s,routeCount:s.length}}function Pe(e,t,a){return a?`${e} ${t.replace("{count}",String(a))}`:e}const Le=4096,Ee=1e6,De=()=>({kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}),Fe=(e,t,a)=>{if(!Number.isInteger(e)||!Number.isInteger(t)||e<=0||t<=0||e>Ee||t>Ee)return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const i=e*t*4;return{kind:Math.max(e,t)>16384?"hard":i>134217728?"warn":"safe",width:e,height:t,alpha:a,decodedBytes:i}},He=(e,t)=>(e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0,Ie=(e,t)=>e[t]<<8|e[t+1],Re=(e,t)=>e[t]|e[t+1]<<8|e[t+2]<<16,Ne=(e,t)=>e[t]|e[t+1]<<8,Oe=(e,t,a)=>{for(let i=0;i<a.length;i++)if(e[t+i]!==a.charCodeAt(i))return!1;return!0};function Be(e,t){try{return"png"===t?function(e){if(e.length<33||2303741511!==He(e,0)||218765834!==He(e,4))return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};if(!Oe(e,12,"IHDR"))return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const t=He(e,16),a=He(e,20),i=e[25];let o=4===i||6===i;if(!o){let t=33;for(let a=0;a<64&&t+8<=e.length;a++){const a=He(e,t);if(Oe(e,t+4,"tRNS")){o=!0;break}if(Oe(e,t+4,"IDAT")||Oe(e,t+4,"IEND"))break;if(a>e.length)break;t+=12+a}}return Fe(t,a,o)}(e):"jpg"===t?function(e){if(e.length<4||255!==e[0]||216!==e[1])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};let t=2;for(let a=0;a<256&&t+4<=e.length;a++){if(255!==e[t]){t++;continue}const a=e[t+1];if(216===a||a>=208&&a<=217){t+=2;continue}if(a>=192&&a<=207&&196!==a&&200!==a&&204!==a)return t+9>e.length?De():Fe(Ie(e,t+7),Ie(e,t+5),!1);if(218===a)return De();const i=Ie(e,t+2);if(i<2)return De();t+=2+i}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):"webp"===t?function(e){if(e.length<30||!Oe(e,0,"RIFF")||!Oe(e,8,"WEBP"))return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};if(Oe(e,12,"VP8X")){const t=!!(16&e[20]);return Fe(Re(e,24)+1,Re(e,27)+1,t)}if(Oe(e,12,"VP8 "))return 157!==e[23]||1!==e[24]||42!==e[25]?{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}:Fe(16383&Ne(e,26),16383&Ne(e,28),!1);if(Oe(e,12,"VP8L")){if(47!==e[20])return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null};const t=e[21]|e[22]<<8|e[23]<<16|e[24]<<24;return Fe(1+(16383&t),1+(t>>14&16383),1==(t>>28&1))}return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}(e):{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}catch{return{kind:"unknown",width:null,height:null,alpha:!1,decodedBytes:null}}}function ze(e,t,a=4096){const i=Math.min(1,a/Math.max(e,t));return{width:Math.max(1,Math.round(e*i)),height:Math.max(1,Math.round(t*i))}}const qe=8388608,Ae="/api/houseplan/plans/upload",Ue={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"};async function je(e,t=1.414){const a=URL.createObjectURL(e);try{return await new Promise(e=>{const i=new Image;i.onload=()=>e(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:t),i.onerror=()=>e(t),i.src=a})}finally{URL.revokeObjectURL(a)}}async function We(e,t,a){return{ext:t,blob:e,aspect:await je(e),name:a}}async function Ve(e,t){const a=(t,a)=>(e._showToast(e._t(t,a)),null),i=await Ye(t,qe);return"reject"===i.kind?a("toast.plan_formats"):"guard"===i.kind?(e._backdropGuard=i.state,null):t.size>qe?a("toast.plan_too_large",{mb:8}):We(t,i.ext,t.name)}async function Ge(e,t,a,i){const o=new FormData;o.append("space_id",a),o.append("ext",i.ext),o.append("file",i.blob,i.name||`plan.${i.ext}`);const n=e?.fetchWithAuth?await e.fetchWithAuth(Ae,{method:"POST",body:o}):await fetch(Ae,{method:"POST",body:o,headers:e?.auth?.data?.access_token?{authorization:`Bearer ${e.auth.data.access_token}`}:{}}),s=await n.json().catch(()=>({}));if(!n.ok||s.error||"string"!=typeof s.url){const e=s.error||(413===n.status?"too_large":""),a={too_large:t("err.too_large",{mb:s.max_mb||8}),bad_ext:t("err.bad_ext"),unauthorized:t("err.unauthorized")};throw new Error(a[e]||e||`HTTP ${n.status}`)}return{url:s.url}}async function Ye(e,t=1/0){const a=function(e){return Ue[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"")}(e);if(!a)return{kind:"reject"};if("svg"===a)return{kind:"pass",ext:a};const i=Be(new Uint8Array(await e.arrayBuffer()),a);return"safe"===i.kind&&e.size<=t?{kind:"pass",ext:a}:{kind:"guard",state:{file:e,ext:a,probe:i,busy:!1}}}async function Ze(e){const t=createImageBitmap(e.file,{imageOrientation:"from-image"}),a=await Promise.race([t,new Promise((e,t)=>{setTimeout(()=>t(new Error("decode timeout")),globalThis.__HP_BACKDROP_TIMEOUT_MS??1e4)})]);try{const{width:t,height:i}=ze(a.width,a.height,Le),o=e.probe.alpha,n=o?"image/png":"image/jpeg";let s;if("function"==typeof OffscreenCanvas){const e=new OffscreenCanvas(t,i),o=e.getContext("2d");if(!o)throw new Error("no 2d context");o.drawImage(a,0,0,t,i),s=await e.convertToBlob({type:n,quality:.9})}else{const e=document.createElement("canvas");e.width=t,e.height=i;const o=e.getContext("2d");if(!o)throw new Error("no 2d context");o.drawImage(a,0,0,t,i),s=await new Promise((t,a)=>{e.toBlob(e=>e?t(e):a(new Error("encode failed")),n,.9)})}const l=o?"png":"jpg";return{blob:s,ext:l,name:`${e.file.name.replace(/\.[^.]+$/,"")||"plan"}-reduced.${l}`}}finally{a.close()}}const Ke=e=>(e/1048576).toFixed(e>=10485760?0:1);function Qe(e,a,i,o,n,s=!0,l){const r=e._backdropGuard;if(!r)return null;const{probe:c}=r,p="hard"===c.kind,d=c.width&&c.height?ze(c.width,c.height,Le):null,h=void 0!==l&&r.file.size>l,u=p?e._t("backdrop.too_large_body",{w:c.width??0,h:c.height??0,limit:16384}):"unknown"===c.kind?e._t("backdrop.unknown_body"):h?e._t("backdrop.over_limit_body",{fileMb:Ke(r.file.size),mb:(l??0)/1048576}):e._t("backdrop.large_body",{w:c.width??0,h:c.height??0,fileMb:Ke(r.file.size),decodedMb:Ke(c.decodedBytes??0)}),m=()=>{e._backdropGuard?.busy||i()},g=()=>e._backdropGuard?.file===r.file;return t`<hp-dialog .hass=${o} data-kind="decor"
|
||
.title=${e._t(p?"backdrop.too_large_title":"backdrop.large_title")}
|
||
icon="mdi:image-size-select-large" dismiss-on-scrim @hp-close=${()=>m()}>
|
||
<div class="body"><p>${u}</p>
|
||
${d?t`<p>${e._t("backdrop.reduced_dimensions",{w:d.width,h:d.height})}</p>`:null}
|
||
</div>
|
||
<div class="row" slot="footer">
|
||
<button class="btn ghost" data-hp="dialog-cancel"
|
||
?disabled=${r.busy} @click=${()=>m()}>
|
||
${e._t("btn.cancel")}</button>
|
||
<span class="spacer"></span>
|
||
${p?null:t`
|
||
${s?t`
|
||
<button class="btn ghost" data-hp="dialog-confirm"
|
||
?disabled=${r.busy} @click=${()=>(async()=>{if(e._backdropGuard?.busy)return;if(n)return e._backdropGuard={...r,busy:!0},e.requestUpdate?.(),await n(r.file,r.file.name),void(g()&&i());const t=await We(r.file,r.ext,r.file.name);g()&&(a(t),i())})()}>
|
||
${e._t("backdrop.keep_original")}</button>`:null}
|
||
<button class="btn on" data-hp="dialog-confirm"
|
||
?disabled=${r.busy} @click=${()=>(async()=>{if(!e._backdropGuard?.busy){e._backdropGuard={...r,busy:!0},e.requestUpdate?.();try{const t=await Ze(r);if(n)return await n(t.blob,t.name),void(g()&&i());if(void 0!==l&&t.blob.size>l){if(!g())return;return i(),void e._showToast(e._t("toast.plan_too_large",{mb:l/1048576}))}const o=await We(t.blob,t.ext,t.name);if(!g())return;a(o),i()}catch{if(!g())return;i(),e._showToast(e._t("backdrop.downscale_failed"))}}})()}>
|
||
${r.busy?e._t("backdrop.reducing"):e._t("backdrop.use_downscaled")}
|
||
</button>`}
|
||
</div>
|
||
</hp-dialog>`}function Je(e,t,a,i){return Qe(e,t,a,i,void 0,(e._backdropGuard?.file.size??0)<=qe,qe)}export{Ve as A,Qe as B,Je as C,ue as D,Ge as E,Ce as F,me as G,ge as H,Me as I,Pe as J,ne as S,ae as a,O as b,Y as c,q as d,I as e,R as f,z as g,U as h,N as i,te as j,B as k,J as l,ee as m,be as n,W as o,V as p,re as q,Q as r,ce as s,X as t,Z as u,j as v,ke as w,xe as x,Ye as y,he as z};
|