3117 lines
109 KiB
JavaScript
3117 lines
109 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
const oe = globalThis, ge = oe.ShadowRoot && (oe.ShadyCSS === void 0 || oe.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, _e = Symbol(), xe = /* @__PURE__ */ new WeakMap();
|
|
let Be = class {
|
|
constructor(e, i, n) {
|
|
if (this._$cssResult$ = !0, n !== _e) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");
|
|
this.cssText = e, this.t = i;
|
|
}
|
|
get styleSheet() {
|
|
let e = this.o;
|
|
const i = this.t;
|
|
if (ge && e === void 0) {
|
|
const n = i !== void 0 && i.length === 1;
|
|
n && (e = xe.get(i)), e === void 0 && ((this.o = e = new CSSStyleSheet()).replaceSync(this.cssText), n && xe.set(i, e));
|
|
}
|
|
return e;
|
|
}
|
|
toString() {
|
|
return this.cssText;
|
|
}
|
|
};
|
|
const rt = (t) => new Be(typeof t == "string" ? t : t + "", void 0, _e), Fe = (t, ...e) => {
|
|
const i = t.length === 1 ? t[0] : e.reduce(((n, s, o) => n + ((r) => {
|
|
if (r._$cssResult$ === !0) return r.cssText;
|
|
if (typeof r == "number") return r;
|
|
throw Error("Value passed to 'css' function must be a 'css' function result: " + r + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.");
|
|
})(s) + t[o + 1]), t[0]);
|
|
return new Be(i, t, _e);
|
|
}, at = (t, e) => {
|
|
if (ge) t.adoptedStyleSheets = e.map(((i) => i instanceof CSSStyleSheet ? i : i.styleSheet));
|
|
else for (const i of e) {
|
|
const n = document.createElement("style"), s = oe.litNonce;
|
|
s !== void 0 && n.setAttribute("nonce", s), n.textContent = i.cssText, t.appendChild(n);
|
|
}
|
|
}, we = ge ? (t) => t : (t) => t instanceof CSSStyleSheet ? ((e) => {
|
|
let i = "";
|
|
for (const n of e.cssRules) i += n.cssText;
|
|
return rt(i);
|
|
})(t) : t;
|
|
/**
|
|
* @license
|
|
* Copyright 2017 Google LLC
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
const { is: lt, defineProperty: ct, getOwnPropertyDescriptor: ht, getOwnPropertyNames: ut, getOwnPropertySymbols: dt, getPrototypeOf: ft } = Object, E = globalThis, Ae = E.trustedTypes, pt = Ae ? Ae.emptyScript : "", gt = E.reactiveElementPolyfillSupport, J = (t, e) => t, se = { toAttribute(t, e) {
|
|
switch (e) {
|
|
case Boolean:
|
|
t = t ? pt : null;
|
|
break;
|
|
case Object:
|
|
case Array:
|
|
t = t == null ? t : JSON.stringify(t);
|
|
}
|
|
return t;
|
|
}, fromAttribute(t, e) {
|
|
let i = t;
|
|
switch (e) {
|
|
case Boolean:
|
|
i = t !== null;
|
|
break;
|
|
case Number:
|
|
i = t === null ? null : Number(t);
|
|
break;
|
|
case Object:
|
|
case Array:
|
|
try {
|
|
i = JSON.parse(t);
|
|
} catch {
|
|
i = null;
|
|
}
|
|
}
|
|
return i;
|
|
} }, me = (t, e) => !lt(t, e), ke = { attribute: !0, type: String, converter: se, reflect: !1, useDefault: !1, hasChanged: me };
|
|
Symbol.metadata ?? (Symbol.metadata = Symbol("metadata")), E.litPropertyMetadata ?? (E.litPropertyMetadata = /* @__PURE__ */ new WeakMap());
|
|
let U = class extends HTMLElement {
|
|
static addInitializer(e) {
|
|
this._$Ei(), (this.l ?? (this.l = [])).push(e);
|
|
}
|
|
static get observedAttributes() {
|
|
return this.finalize(), this._$Eh && [...this._$Eh.keys()];
|
|
}
|
|
static createProperty(e, i = ke) {
|
|
if (i.state && (i.attribute = !1), this._$Ei(), this.prototype.hasOwnProperty(e) && ((i = Object.create(i)).wrapped = !0), this.elementProperties.set(e, i), !i.noAccessor) {
|
|
const n = Symbol(), s = this.getPropertyDescriptor(e, n, i);
|
|
s !== void 0 && ct(this.prototype, e, s);
|
|
}
|
|
}
|
|
static getPropertyDescriptor(e, i, n) {
|
|
const { get: s, set: o } = ht(this.prototype, e) ?? { get() {
|
|
return this[i];
|
|
}, set(r) {
|
|
this[i] = r;
|
|
} };
|
|
return { get: s, set(r) {
|
|
const a = s?.call(this);
|
|
o?.call(this, r), this.requestUpdate(e, a, n);
|
|
}, configurable: !0, enumerable: !0 };
|
|
}
|
|
static getPropertyOptions(e) {
|
|
return this.elementProperties.get(e) ?? ke;
|
|
}
|
|
static _$Ei() {
|
|
if (this.hasOwnProperty(J("elementProperties"))) return;
|
|
const e = ft(this);
|
|
e.finalize(), e.l !== void 0 && (this.l = [...e.l]), this.elementProperties = new Map(e.elementProperties);
|
|
}
|
|
static finalize() {
|
|
if (this.hasOwnProperty(J("finalized"))) return;
|
|
if (this.finalized = !0, this._$Ei(), this.hasOwnProperty(J("properties"))) {
|
|
const i = this.properties, n = [...ut(i), ...dt(i)];
|
|
for (const s of n) this.createProperty(s, i[s]);
|
|
}
|
|
const e = this[Symbol.metadata];
|
|
if (e !== null) {
|
|
const i = litPropertyMetadata.get(e);
|
|
if (i !== void 0) for (const [n, s] of i) this.elementProperties.set(n, s);
|
|
}
|
|
this._$Eh = /* @__PURE__ */ new Map();
|
|
for (const [i, n] of this.elementProperties) {
|
|
const s = this._$Eu(i, n);
|
|
s !== void 0 && this._$Eh.set(s, i);
|
|
}
|
|
this.elementStyles = this.finalizeStyles(this.styles);
|
|
}
|
|
static finalizeStyles(e) {
|
|
const i = [];
|
|
if (Array.isArray(e)) {
|
|
const n = new Set(e.flat(1 / 0).reverse());
|
|
for (const s of n) i.unshift(we(s));
|
|
} else e !== void 0 && i.push(we(e));
|
|
return i;
|
|
}
|
|
static _$Eu(e, i) {
|
|
const n = i.attribute;
|
|
return n === !1 ? void 0 : typeof n == "string" ? n : typeof e == "string" ? e.toLowerCase() : void 0;
|
|
}
|
|
constructor() {
|
|
super(), this._$Ep = void 0, this.isUpdatePending = !1, this.hasUpdated = !1, this._$Em = null, this._$Ev();
|
|
}
|
|
_$Ev() {
|
|
this._$ES = new Promise(((e) => this.enableUpdating = e)), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), this.constructor.l?.forEach(((e) => e(this)));
|
|
}
|
|
addController(e) {
|
|
(this._$EO ?? (this._$EO = /* @__PURE__ */ new Set())).add(e), this.renderRoot !== void 0 && this.isConnected && e.hostConnected?.();
|
|
}
|
|
removeController(e) {
|
|
this._$EO?.delete(e);
|
|
}
|
|
_$E_() {
|
|
const e = /* @__PURE__ */ new Map(), i = this.constructor.elementProperties;
|
|
for (const n of i.keys()) this.hasOwnProperty(n) && (e.set(n, this[n]), delete this[n]);
|
|
e.size > 0 && (this._$Ep = e);
|
|
}
|
|
createRenderRoot() {
|
|
const e = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions);
|
|
return at(e, this.constructor.elementStyles), e;
|
|
}
|
|
connectedCallback() {
|
|
this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this.enableUpdating(!0), this._$EO?.forEach(((e) => e.hostConnected?.()));
|
|
}
|
|
enableUpdating(e) {
|
|
}
|
|
disconnectedCallback() {
|
|
this._$EO?.forEach(((e) => e.hostDisconnected?.()));
|
|
}
|
|
attributeChangedCallback(e, i, n) {
|
|
this._$AK(e, n);
|
|
}
|
|
_$ET(e, i) {
|
|
const n = this.constructor.elementProperties.get(e), s = this.constructor._$Eu(e, n);
|
|
if (s !== void 0 && n.reflect === !0) {
|
|
const o = (n.converter?.toAttribute !== void 0 ? n.converter : se).toAttribute(i, n.type);
|
|
this._$Em = e, o == null ? this.removeAttribute(s) : this.setAttribute(s, o), this._$Em = null;
|
|
}
|
|
}
|
|
_$AK(e, i) {
|
|
const n = this.constructor, s = n._$Eh.get(e);
|
|
if (s !== void 0 && this._$Em !== s) {
|
|
const o = n.getPropertyOptions(s), r = typeof o.converter == "function" ? { fromAttribute: o.converter } : o.converter?.fromAttribute !== void 0 ? o.converter : se;
|
|
this._$Em = s;
|
|
const a = r.fromAttribute(i, o.type);
|
|
this[s] = a ?? this._$Ej?.get(s) ?? a, this._$Em = null;
|
|
}
|
|
}
|
|
requestUpdate(e, i, n) {
|
|
if (e !== void 0) {
|
|
const s = this.constructor, o = this[e];
|
|
if (n ?? (n = s.getPropertyOptions(e)), !((n.hasChanged ?? me)(o, i) || n.useDefault && n.reflect && o === this._$Ej?.get(e) && !this.hasAttribute(s._$Eu(e, n)))) return;
|
|
this.C(e, i, n);
|
|
}
|
|
this.isUpdatePending === !1 && (this._$ES = this._$EP());
|
|
}
|
|
C(e, i, { useDefault: n, reflect: s, wrapped: o }, r) {
|
|
n && !(this._$Ej ?? (this._$Ej = /* @__PURE__ */ new Map())).has(e) && (this._$Ej.set(e, r ?? i ?? this[e]), o !== !0 || r !== void 0) || (this._$AL.has(e) || (this.hasUpdated || n || (i = void 0), this._$AL.set(e, i)), s === !0 && this._$Em !== e && (this._$Eq ?? (this._$Eq = /* @__PURE__ */ new Set())).add(e));
|
|
}
|
|
async _$EP() {
|
|
this.isUpdatePending = !0;
|
|
try {
|
|
await this._$ES;
|
|
} catch (i) {
|
|
Promise.reject(i);
|
|
}
|
|
const e = this.scheduleUpdate();
|
|
return e != null && await e, !this.isUpdatePending;
|
|
}
|
|
scheduleUpdate() {
|
|
return this.performUpdate();
|
|
}
|
|
performUpdate() {
|
|
if (!this.isUpdatePending) return;
|
|
if (!this.hasUpdated) {
|
|
if (this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this._$Ep) {
|
|
for (const [s, o] of this._$Ep) this[s] = o;
|
|
this._$Ep = void 0;
|
|
}
|
|
const n = this.constructor.elementProperties;
|
|
if (n.size > 0) for (const [s, o] of n) {
|
|
const { wrapped: r } = o, a = this[s];
|
|
r !== !0 || this._$AL.has(s) || a === void 0 || this.C(s, void 0, o, a);
|
|
}
|
|
}
|
|
let e = !1;
|
|
const i = this._$AL;
|
|
try {
|
|
e = this.shouldUpdate(i), e ? (this.willUpdate(i), this._$EO?.forEach(((n) => n.hostUpdate?.())), this.update(i)) : this._$EM();
|
|
} catch (n) {
|
|
throw e = !1, this._$EM(), n;
|
|
}
|
|
e && this._$AE(i);
|
|
}
|
|
willUpdate(e) {
|
|
}
|
|
_$AE(e) {
|
|
this._$EO?.forEach(((i) => i.hostUpdated?.())), this.hasUpdated || (this.hasUpdated = !0, this.firstUpdated(e)), this.updated(e);
|
|
}
|
|
_$EM() {
|
|
this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = !1;
|
|
}
|
|
get updateComplete() {
|
|
return this.getUpdateComplete();
|
|
}
|
|
getUpdateComplete() {
|
|
return this._$ES;
|
|
}
|
|
shouldUpdate(e) {
|
|
return !0;
|
|
}
|
|
update(e) {
|
|
this._$Eq && (this._$Eq = this._$Eq.forEach(((i) => this._$ET(i, this[i])))), this._$EM();
|
|
}
|
|
updated(e) {
|
|
}
|
|
firstUpdated(e) {
|
|
}
|
|
};
|
|
U.elementStyles = [], U.shadowRootOptions = { mode: "open" }, U[J("elementProperties")] = /* @__PURE__ */ new Map(), U[J("finalized")] = /* @__PURE__ */ new Map(), gt?.({ ReactiveElement: U }), (E.reactiveElementVersions ?? (E.reactiveElementVersions = [])).push("2.1.1");
|
|
/**
|
|
* @license
|
|
* Copyright 2017 Google LLC
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
const Z = globalThis, re = Z.trustedTypes, Ce = re ? re.createPolicy("lit-html", { createHTML: (t) => t }) : void 0, je = "$lit$", V = `lit$${Math.random().toFixed(9).slice(2)}$`, qe = "?" + V, _t = `<${qe}>`, I = document, Q = () => I.createComment(""), K = (t) => t === null || typeof t != "object" && typeof t != "function", be = Array.isArray, mt = (t) => be(t) || typeof t?.[Symbol.iterator] == "function", de = `[
|
|
\f\r]`, W = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, Se = /-->/g, ze = />/g, P = RegExp(`>|${de}(?:([^\\s"'>=/]+)(${de}*=${de}*(?:[^
|
|
\f\r"'\`<>=]|("|')|))|$)`, "g"), Me = /'/g, Ve = /"/g, We = /^(?:script|style|textarea|title)$/i, Ge = (t) => (e, ...i) => ({ _$litType$: t, strings: e, values: i }), x = Ge(1), D = Ge(2), j = Symbol.for("lit-noChange"), _ = Symbol.for("lit-nothing"), Ee = /* @__PURE__ */ new WeakMap(), O = I.createTreeWalker(I, 129);
|
|
function Ye(t, e) {
|
|
if (!be(t) || !t.hasOwnProperty("raw")) throw Error("invalid template strings array");
|
|
return Ce !== void 0 ? Ce.createHTML(e) : e;
|
|
}
|
|
const bt = (t, e) => {
|
|
const i = t.length - 1, n = [];
|
|
let s, o = e === 2 ? "<svg>" : e === 3 ? "<math>" : "", r = W;
|
|
for (let a = 0; a < i; a++) {
|
|
const l = t[a];
|
|
let h, c, u = -1, f = 0;
|
|
for (; f < l.length && (r.lastIndex = f, c = r.exec(l), c !== null); ) f = r.lastIndex, r === W ? c[1] === "!--" ? r = Se : c[1] !== void 0 ? r = ze : c[2] !== void 0 ? (We.test(c[2]) && (s = RegExp("</" + c[2], "g")), r = P) : c[3] !== void 0 && (r = P) : r === P ? c[0] === ">" ? (r = s ?? W, u = -1) : c[1] === void 0 ? u = -2 : (u = r.lastIndex - c[2].length, h = c[1], r = c[3] === void 0 ? P : c[3] === '"' ? Ve : Me) : r === Ve || r === Me ? r = P : r === Se || r === ze ? r = W : (r = P, s = void 0);
|
|
const d = r === P && t[a + 1].startsWith("/>") ? " " : "";
|
|
o += r === W ? l + _t : u >= 0 ? (n.push(h), l.slice(0, u) + je + l.slice(u) + V + d) : l + V + (u === -2 ? a : d);
|
|
}
|
|
return [Ye(t, o + (t[i] || "<?>") + (e === 2 ? "</svg>" : e === 3 ? "</math>" : "")), n];
|
|
};
|
|
class ee {
|
|
constructor({ strings: e, _$litType$: i }, n) {
|
|
let s;
|
|
this.parts = [];
|
|
let o = 0, r = 0;
|
|
const a = e.length - 1, l = this.parts, [h, c] = bt(e, i);
|
|
if (this.el = ee.createElement(h, n), O.currentNode = this.el.content, i === 2 || i === 3) {
|
|
const u = this.el.content.firstChild;
|
|
u.replaceWith(...u.childNodes);
|
|
}
|
|
for (; (s = O.nextNode()) !== null && l.length < a; ) {
|
|
if (s.nodeType === 1) {
|
|
if (s.hasAttributes()) for (const u of s.getAttributeNames()) if (u.endsWith(je)) {
|
|
const f = c[r++], d = s.getAttribute(u).split(V), p = /([.?@])?(.*)/.exec(f);
|
|
l.push({ type: 1, index: o, name: p[2], strings: d, ctor: p[1] === "." ? yt : p[1] === "?" ? $t : p[1] === "@" ? xt : le }), s.removeAttribute(u);
|
|
} else u.startsWith(V) && (l.push({ type: 6, index: o }), s.removeAttribute(u));
|
|
if (We.test(s.tagName)) {
|
|
const u = s.textContent.split(V), f = u.length - 1;
|
|
if (f > 0) {
|
|
s.textContent = re ? re.emptyScript : "";
|
|
for (let d = 0; d < f; d++) s.append(u[d], Q()), O.nextNode(), l.push({ type: 2, index: ++o });
|
|
s.append(u[f], Q());
|
|
}
|
|
}
|
|
} else if (s.nodeType === 8) if (s.data === qe) l.push({ type: 2, index: o });
|
|
else {
|
|
let u = -1;
|
|
for (; (u = s.data.indexOf(V, u + 1)) !== -1; ) l.push({ type: 7, index: o }), u += V.length - 1;
|
|
}
|
|
o++;
|
|
}
|
|
}
|
|
static createElement(e, i) {
|
|
const n = I.createElement("template");
|
|
return n.innerHTML = e, n;
|
|
}
|
|
}
|
|
function q(t, e, i = t, n) {
|
|
if (e === j) return e;
|
|
let s = n !== void 0 ? i._$Co?.[n] : i._$Cl;
|
|
const o = K(e) ? void 0 : e._$litDirective$;
|
|
return s?.constructor !== o && (s?._$AO?.(!1), o === void 0 ? s = void 0 : (s = new o(t), s._$AT(t, i, n)), n !== void 0 ? (i._$Co ?? (i._$Co = []))[n] = s : i._$Cl = s), s !== void 0 && (e = q(t, s._$AS(t, e.values), s, n)), e;
|
|
}
|
|
class vt {
|
|
constructor(e, i) {
|
|
this._$AV = [], this._$AN = void 0, this._$AD = e, this._$AM = i;
|
|
}
|
|
get parentNode() {
|
|
return this._$AM.parentNode;
|
|
}
|
|
get _$AU() {
|
|
return this._$AM._$AU;
|
|
}
|
|
u(e) {
|
|
const { el: { content: i }, parts: n } = this._$AD, s = (e?.creationScope ?? I).importNode(i, !0);
|
|
O.currentNode = s;
|
|
let o = O.nextNode(), r = 0, a = 0, l = n[0];
|
|
for (; l !== void 0; ) {
|
|
if (r === l.index) {
|
|
let h;
|
|
l.type === 2 ? h = new te(o, o.nextSibling, this, e) : l.type === 1 ? h = new l.ctor(o, l.name, l.strings, this, e) : l.type === 6 && (h = new wt(o, this, e)), this._$AV.push(h), l = n[++a];
|
|
}
|
|
r !== l?.index && (o = O.nextNode(), r++);
|
|
}
|
|
return O.currentNode = I, s;
|
|
}
|
|
p(e) {
|
|
let i = 0;
|
|
for (const n of this._$AV) n !== void 0 && (n.strings !== void 0 ? (n._$AI(e, n, i), i += n.strings.length - 2) : n._$AI(e[i])), i++;
|
|
}
|
|
}
|
|
class te {
|
|
get _$AU() {
|
|
return this._$AM?._$AU ?? this._$Cv;
|
|
}
|
|
constructor(e, i, n, s) {
|
|
this.type = 2, this._$AH = _, this._$AN = void 0, this._$AA = e, this._$AB = i, this._$AM = n, this.options = s, this._$Cv = s?.isConnected ?? !0;
|
|
}
|
|
get parentNode() {
|
|
let e = this._$AA.parentNode;
|
|
const i = this._$AM;
|
|
return i !== void 0 && e?.nodeType === 11 && (e = i.parentNode), e;
|
|
}
|
|
get startNode() {
|
|
return this._$AA;
|
|
}
|
|
get endNode() {
|
|
return this._$AB;
|
|
}
|
|
_$AI(e, i = this) {
|
|
e = q(this, e, i), K(e) ? e === _ || e == null || e === "" ? (this._$AH !== _ && this._$AR(), this._$AH = _) : e !== this._$AH && e !== j && this._(e) : e._$litType$ !== void 0 ? this.$(e) : e.nodeType !== void 0 ? this.T(e) : mt(e) ? this.k(e) : this._(e);
|
|
}
|
|
O(e) {
|
|
return this._$AA.parentNode.insertBefore(e, this._$AB);
|
|
}
|
|
T(e) {
|
|
this._$AH !== e && (this._$AR(), this._$AH = this.O(e));
|
|
}
|
|
_(e) {
|
|
this._$AH !== _ && K(this._$AH) ? this._$AA.nextSibling.data = e : this.T(I.createTextNode(e)), this._$AH = e;
|
|
}
|
|
$(e) {
|
|
const { values: i, _$litType$: n } = e, s = typeof n == "number" ? this._$AC(e) : (n.el === void 0 && (n.el = ee.createElement(Ye(n.h, n.h[0]), this.options)), n);
|
|
if (this._$AH?._$AD === s) this._$AH.p(i);
|
|
else {
|
|
const o = new vt(s, this), r = o.u(this.options);
|
|
o.p(i), this.T(r), this._$AH = o;
|
|
}
|
|
}
|
|
_$AC(e) {
|
|
let i = Ee.get(e.strings);
|
|
return i === void 0 && Ee.set(e.strings, i = new ee(e)), i;
|
|
}
|
|
k(e) {
|
|
be(this._$AH) || (this._$AH = [], this._$AR());
|
|
const i = this._$AH;
|
|
let n, s = 0;
|
|
for (const o of e) s === i.length ? i.push(n = new te(this.O(Q()), this.O(Q()), this, this.options)) : n = i[s], n._$AI(o), s++;
|
|
s < i.length && (this._$AR(n && n._$AB.nextSibling, s), i.length = s);
|
|
}
|
|
_$AR(e = this._$AA.nextSibling, i) {
|
|
for (this._$AP?.(!1, !0, i); e !== this._$AB; ) {
|
|
const n = e.nextSibling;
|
|
e.remove(), e = n;
|
|
}
|
|
}
|
|
setConnected(e) {
|
|
this._$AM === void 0 && (this._$Cv = e, this._$AP?.(e));
|
|
}
|
|
}
|
|
class le {
|
|
get tagName() {
|
|
return this.element.tagName;
|
|
}
|
|
get _$AU() {
|
|
return this._$AM._$AU;
|
|
}
|
|
constructor(e, i, n, s, o) {
|
|
this.type = 1, this._$AH = _, this._$AN = void 0, this.element = e, this.name = i, this._$AM = s, this.options = o, n.length > 2 || n[0] !== "" || n[1] !== "" ? (this._$AH = Array(n.length - 1).fill(new String()), this.strings = n) : this._$AH = _;
|
|
}
|
|
_$AI(e, i = this, n, s) {
|
|
const o = this.strings;
|
|
let r = !1;
|
|
if (o === void 0) e = q(this, e, i, 0), r = !K(e) || e !== this._$AH && e !== j, r && (this._$AH = e);
|
|
else {
|
|
const a = e;
|
|
let l, h;
|
|
for (e = o[0], l = 0; l < o.length - 1; l++) h = q(this, a[n + l], i, l), h === j && (h = this._$AH[l]), r || (r = !K(h) || h !== this._$AH[l]), h === _ ? e = _ : e !== _ && (e += (h ?? "") + o[l + 1]), this._$AH[l] = h;
|
|
}
|
|
r && !s && this.j(e);
|
|
}
|
|
j(e) {
|
|
e === _ ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, e ?? "");
|
|
}
|
|
}
|
|
class yt extends le {
|
|
constructor() {
|
|
super(...arguments), this.type = 3;
|
|
}
|
|
j(e) {
|
|
this.element[this.name] = e === _ ? void 0 : e;
|
|
}
|
|
}
|
|
class $t extends le {
|
|
constructor() {
|
|
super(...arguments), this.type = 4;
|
|
}
|
|
j(e) {
|
|
this.element.toggleAttribute(this.name, !!e && e !== _);
|
|
}
|
|
}
|
|
class xt extends le {
|
|
constructor(e, i, n, s, o) {
|
|
super(e, i, n, s, o), this.type = 5;
|
|
}
|
|
_$AI(e, i = this) {
|
|
if ((e = q(this, e, i, 0) ?? _) === j) return;
|
|
const n = this._$AH, s = e === _ && n !== _ || e.capture !== n.capture || e.once !== n.once || e.passive !== n.passive, o = e !== _ && (n === _ || s);
|
|
s && this.element.removeEventListener(this.name, this, n), o && this.element.addEventListener(this.name, this, e), this._$AH = e;
|
|
}
|
|
handleEvent(e) {
|
|
typeof this._$AH == "function" ? this._$AH.call(this.options?.host ?? this.element, e) : this._$AH.handleEvent(e);
|
|
}
|
|
}
|
|
class wt {
|
|
constructor(e, i, n) {
|
|
this.element = e, this.type = 6, this._$AN = void 0, this._$AM = i, this.options = n;
|
|
}
|
|
get _$AU() {
|
|
return this._$AM._$AU;
|
|
}
|
|
_$AI(e) {
|
|
q(this, e);
|
|
}
|
|
}
|
|
const At = Z.litHtmlPolyfillSupport;
|
|
At?.(ee, te), (Z.litHtmlVersions ?? (Z.litHtmlVersions = [])).push("3.3.1");
|
|
const kt = (t, e, i) => {
|
|
const n = i?.renderBefore ?? e;
|
|
let s = n._$litPart$;
|
|
if (s === void 0) {
|
|
const o = i?.renderBefore ?? null;
|
|
n._$litPart$ = s = new te(e.insertBefore(Q(), o), o, void 0, i ?? {});
|
|
}
|
|
return s._$AI(t), s;
|
|
};
|
|
/**
|
|
* @license
|
|
* Copyright 2017 Google LLC
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
const X = globalThis;
|
|
class B extends U {
|
|
constructor() {
|
|
super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0;
|
|
}
|
|
createRenderRoot() {
|
|
var i;
|
|
const e = super.createRenderRoot();
|
|
return (i = this.renderOptions).renderBefore ?? (i.renderBefore = e.firstChild), e;
|
|
}
|
|
update(e) {
|
|
const i = this.render();
|
|
this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(e), this._$Do = kt(i, this.renderRoot, this.renderOptions);
|
|
}
|
|
connectedCallback() {
|
|
super.connectedCallback(), this._$Do?.setConnected(!0);
|
|
}
|
|
disconnectedCallback() {
|
|
super.disconnectedCallback(), this._$Do?.setConnected(!1);
|
|
}
|
|
render() {
|
|
return j;
|
|
}
|
|
}
|
|
B._$litElement$ = !0, B.finalized = !0, X.litElementHydrateSupport?.({ LitElement: B });
|
|
const Ct = X.litElementPolyfillSupport;
|
|
Ct?.({ LitElement: B });
|
|
(X.litElementVersions ?? (X.litElementVersions = [])).push("4.2.1");
|
|
/**
|
|
* @license
|
|
* Copyright 2017 Google LLC
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
const Je = (t) => (e, i) => {
|
|
i !== void 0 ? i.addInitializer((() => {
|
|
customElements.define(t, e);
|
|
})) : customElements.define(t, e);
|
|
};
|
|
/**
|
|
* @license
|
|
* Copyright 2017 Google LLC
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
const St = { attribute: !0, type: String, converter: se, reflect: !1, hasChanged: me }, zt = (t = St, e, i) => {
|
|
const { kind: n, metadata: s } = i;
|
|
let o = globalThis.litPropertyMetadata.get(s);
|
|
if (o === void 0 && globalThis.litPropertyMetadata.set(s, o = /* @__PURE__ */ new Map()), n === "setter" && ((t = Object.create(t)).wrapped = !0), o.set(i.name, t), n === "accessor") {
|
|
const { name: r } = i;
|
|
return { set(a) {
|
|
const l = e.get.call(this);
|
|
e.set.call(this, a), this.requestUpdate(r, l, t);
|
|
}, init(a) {
|
|
return a !== void 0 && this.C(r, void 0, t, a), a;
|
|
} };
|
|
}
|
|
if (n === "setter") {
|
|
const { name: r } = i;
|
|
return function(a) {
|
|
const l = this[r];
|
|
e.call(this, a), this.requestUpdate(r, l, t);
|
|
};
|
|
}
|
|
throw Error("Unsupported decorator location: " + n);
|
|
};
|
|
function ce(t) {
|
|
return (e, i) => typeof i == "object" ? zt(t, e, i) : ((n, s, o) => {
|
|
const r = s.hasOwnProperty(o);
|
|
return s.constructor.createProperty(o, n), r ? Object.getOwnPropertyDescriptor(s, o) : void 0;
|
|
})(t, e, i);
|
|
}
|
|
/**
|
|
* @license
|
|
* Copyright 2017 Google LLC
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
function ve(t) {
|
|
return ce({ ...t, state: !0, attribute: !1 });
|
|
}
|
|
var Te, Pe;
|
|
(function(t) {
|
|
t.language = "language", t.system = "system", t.comma_decimal = "comma_decimal", t.decimal_comma = "decimal_comma", t.space_comma = "space_comma", t.none = "none";
|
|
})(Te || (Te = {})), (function(t) {
|
|
t.language = "language", t.system = "system", t.am_pm = "12", t.twenty_four = "24";
|
|
})(Pe || (Pe = {}));
|
|
var pe = function(t, e, i, n) {
|
|
n = n || {}, i = i ?? {};
|
|
var s = new Event(e, { bubbles: n.bubbles === void 0 || n.bubbles, cancelable: !!n.cancelable, composed: n.composed === void 0 || n.composed });
|
|
return s.detail = i, t.dispatchEvent(s), s;
|
|
}, Mt = function(t, e, i) {
|
|
var n;
|
|
return i === void 0 && (i = !1), function() {
|
|
var s = [].slice.call(arguments), o = this, r = function() {
|
|
n = null, i || t.apply(o, s);
|
|
}, a = i && !n;
|
|
clearTimeout(n), n = setTimeout(r, e), a && t.apply(o, s);
|
|
};
|
|
}, Vt = function(t, e, i) {
|
|
i === void 0 && (i = !1), i ? history.replaceState(null, "", e) : history.pushState(null, "", e), pe(window, "location-changed", { replace: i });
|
|
};
|
|
function Ze(t) {
|
|
return typeof t == "object" && t !== null && "entity" in t && typeof t.entity == "string";
|
|
}
|
|
function Et(t) {
|
|
return Array.isArray(t) && t.every((e) => typeof e == "string");
|
|
}
|
|
function Tt(t) {
|
|
return Array.isArray(t) && t.length === 3 && t.every((e) => typeof e == "number");
|
|
}
|
|
function Pt(t) {
|
|
return Array.isArray(t) && t.length === 2 && t.every((e) => typeof e == "number");
|
|
}
|
|
function Nt(t) {
|
|
return typeof t == "string" && (t === "heating" || t === "cooling" || t === "idle" || t === "off");
|
|
}
|
|
function Dt(t) {
|
|
return Nt(t) ? t : void 0;
|
|
}
|
|
function F(t) {
|
|
if (typeof t == "number" && !Number.isNaN(t))
|
|
return t;
|
|
}
|
|
function L(t) {
|
|
if (typeof t == "string")
|
|
return t;
|
|
}
|
|
function Ot(t) {
|
|
return Et(t) ? t : [];
|
|
}
|
|
function It(t) {
|
|
if (Tt(t))
|
|
return t;
|
|
}
|
|
function Ht(t) {
|
|
if (Pt(t))
|
|
return t;
|
|
}
|
|
function ie(t, e) {
|
|
return e.includes("*") ? new RegExp(`^${e.replaceAll("*", ".*")}$`).test(t) : t === e;
|
|
}
|
|
const Rt = /* @__PURE__ */ new Set(["unavailable", "unknown", "none"]);
|
|
function Lt(t) {
|
|
return Rt.has(t.toLowerCase());
|
|
}
|
|
function Ut(t, e) {
|
|
const i = t / 360, n = e / 100, s = 0.5;
|
|
let o, r, a;
|
|
if (n === 0)
|
|
o = r = a = s;
|
|
else {
|
|
const l = (u, f, d) => (d < 0 && (d += 1), d > 1 && (d -= 1), d < 0.16666666666666666 ? u + (f - u) * 6 * d : d < 0.5 ? f : d < 0.6666666666666666 ? u + (f - u) * (0.6666666666666666 - d) * 6 : u), h = s + n - s * n, c = 2 * s - h;
|
|
o = l(c, h, i + 1 / 3), r = l(c, h, i), a = l(c, h, i - 1 / 3);
|
|
}
|
|
return [Math.round(o * 255), Math.round(r * 255), Math.round(a * 255)];
|
|
}
|
|
function ye(t) {
|
|
if (t.startsWith("#")) {
|
|
const i = t.replaceAll("#", "");
|
|
return [
|
|
Number.parseInt(i.slice(0, 2), 16),
|
|
Number.parseInt(i.slice(2, 4), 16),
|
|
Number.parseInt(i.slice(4, 6), 16)
|
|
];
|
|
}
|
|
const e = /rgba?\((\d+),\s*(\d+),\s*(\d+)/.exec(t);
|
|
return e ? [
|
|
Number.parseInt(e[1] ?? "0", 10),
|
|
Number.parseInt(e[2] ?? "0", 10),
|
|
Number.parseInt(e[3] ?? "0", 10)
|
|
] : null;
|
|
}
|
|
function Bt(t, e, i) {
|
|
return (0.299 * t + 0.587 * e + 0.114 * i) / 255;
|
|
}
|
|
function Ft(t) {
|
|
const e = ye(t);
|
|
return (e ? Bt(e[0], e[1], e[2]) > 0.5 : !1) ? {
|
|
icon: "rgba(0, 0, 0, 0.7)",
|
|
label: "rgba(0, 0, 0, 0.9)",
|
|
value: "rgba(0, 0, 0, 0.7)"
|
|
} : {
|
|
icon: "rgba(255, 255, 255, 0.85)",
|
|
label: "rgba(255, 255, 255, 0.95)",
|
|
value: "rgba(255, 255, 255, 0.85)"
|
|
};
|
|
}
|
|
function S(t, e, i, n) {
|
|
const s = t.replaceAll("#", ""), o = e.replaceAll("#", ""), r = Number.parseInt(s.slice(0, 2), 16), a = Number.parseInt(s.slice(2, 4), 16), l = Number.parseInt(s.slice(4, 6), 16), h = Number.parseInt(o.slice(0, 2), 16), c = Number.parseInt(o.slice(2, 4), 16), u = Number.parseInt(o.slice(4, 6), 16), f = Math.round(r + (h - r) * i), d = Math.round(a + (c - a) * i), p = Math.round(l + (u - l) * i);
|
|
return n !== void 0 ? `rgba(${f}, ${d}, ${p}, ${n})` : `rgb(${f}, ${d}, ${p})`;
|
|
}
|
|
function jt(t) {
|
|
const { clampedValue: e, minValue: i, maxValue: n, midPoint: s, colorLow: o, colorMid: r, colorHigh: a, opacity: l } = t;
|
|
if (e <= s) {
|
|
if (i >= s)
|
|
return S(r, r, 1, l);
|
|
const h = (e - i) / (s - i);
|
|
return S(o, r, h, l);
|
|
} else {
|
|
if (n <= s)
|
|
return S(r, r, 1, l);
|
|
const h = (e - s) / (n - s);
|
|
return S(r, a, h, l);
|
|
}
|
|
}
|
|
function qt(t) {
|
|
const { clampedValue: e, minValue: i, maxValue: n, neutral: s, colorLow: o, colorHigh: r, opacity: a } = t;
|
|
if (e <= s) {
|
|
if (i >= s) return S(o, r, 0.5, a);
|
|
const l = (e - i) / (s - i);
|
|
return S(o, r, l * 0.5, a);
|
|
} else {
|
|
if (n <= s) return S(o, r, 0.5, a);
|
|
const l = (e - s) / (n - s);
|
|
return S(o, r, 0.5 + l * 0.5, a);
|
|
}
|
|
}
|
|
function Wt(t, e, i, n) {
|
|
const { colorHigh: s, colorLow: o, colorMid: r, opacity: a } = n, l = n.scaleMin ?? e, h = n.scaleMax ?? i, c = n.neutral, u = Math.max(l, Math.min(h, t)), f = c ?? (l + h) / 2;
|
|
if (r)
|
|
return jt({
|
|
clampedValue: u,
|
|
minValue: l,
|
|
maxValue: h,
|
|
midPoint: f,
|
|
colorLow: o,
|
|
colorMid: r,
|
|
colorHigh: s,
|
|
opacity: a
|
|
});
|
|
if (c !== void 0)
|
|
return qt({
|
|
clampedValue: u,
|
|
minValue: l,
|
|
maxValue: h,
|
|
neutral: c,
|
|
colorLow: o,
|
|
colorHigh: s,
|
|
opacity: a
|
|
});
|
|
if (h === l)
|
|
return S(s, s, 1, a);
|
|
const d = (u - l) / (h - l);
|
|
return S(o, s, d, a);
|
|
}
|
|
function G(t, e) {
|
|
const i = ye(t);
|
|
return i ? `rgba(${i[0]}, ${i[1]}, ${i[2]}, ${e})` : t;
|
|
}
|
|
function Gt(t) {
|
|
return t.startsWith("light.");
|
|
}
|
|
function Yt({ attributes: t, state: e }) {
|
|
const i = e === "on", n = F(t.brightness), s = i ? Math.round((n ?? 255) / 255 * 100) : 0, r = Ot(t.supported_color_modes).some(
|
|
(l) => ["rgb", "rgbw", "rgbww", "hs", "xy"].includes(l)
|
|
), a = {
|
|
brightness: s,
|
|
isOn: i,
|
|
supportsColor: r
|
|
};
|
|
if (i && r) {
|
|
const l = It(t.rgb_color), h = Ht(t.hs_color);
|
|
l ? a.rgb = l : h && (a.hs = h);
|
|
}
|
|
return a;
|
|
}
|
|
function Jt(t, e, i) {
|
|
if (!t || !t.isOn)
|
|
return e;
|
|
const n = 0.3 + t.brightness / 100 * 0.7;
|
|
if (t.supportsColor && t.rgb) {
|
|
const [o, r, a] = t.rgb;
|
|
return `rgba(${o}, ${r}, ${a}, ${n})`;
|
|
}
|
|
if (t.supportsColor && t.hs) {
|
|
const [o, r] = t.hs, [a, l, h] = Ut(o, r);
|
|
return `rgba(${a}, ${l}, ${h}, ${n})`;
|
|
}
|
|
const s = ye(i);
|
|
return s ? `rgba(${s[0]}, ${s[1]}, ${s[2]}, ${n})` : i;
|
|
}
|
|
const Zt = ["switch", "input_boolean", "binary_sensor"];
|
|
function Xt(t) {
|
|
const e = t.split(".")[0];
|
|
return Zt.includes(e ?? "");
|
|
}
|
|
function Qt({ state: t }) {
|
|
return { isOn: t === "on" };
|
|
}
|
|
function Kt(t, e, i) {
|
|
return t ? i : e;
|
|
}
|
|
function ei(t) {
|
|
return t.startsWith("climate.");
|
|
}
|
|
function ti({ attributes: t, state: e }) {
|
|
const i = F(t.current_temperature), n = F(t.temperature), s = Dt(t.hvac_action), o = e, r = i ?? null, a = n ?? null;
|
|
let l = 0, h = 0;
|
|
if (r !== null && a !== null) {
|
|
const c = r - a;
|
|
o === "heat" ? h = Math.min(c, 0) : o === "cool" ? h = Math.max(c, 0) : h = c, l = Math.abs(h);
|
|
}
|
|
return {
|
|
currentTemperature: r,
|
|
targetTemperature: a,
|
|
tempDifference: l,
|
|
tempOffset: h,
|
|
hvacAction: s ?? null,
|
|
hvacMode: o ?? null
|
|
};
|
|
}
|
|
function fe(t, e) {
|
|
return e === "temp_difference" ? t.tempDifference : e === "temp_offset" ? t.tempOffset : e === "current_temperature" ? t.currentTemperature : e === "temperature" ? t.targetTemperature : e === "hvac_action" ? t.hvacAction : e === "hvac_mode" ? t.hvacMode : null;
|
|
}
|
|
function ii(t, e = {}, i = 0) {
|
|
const { width: n = 100, height: s = 20 } = e, o = 1, r = s - i, a = e.min ?? Math.min(...t), h = (e.max ?? Math.max(...t)) - a || 1, c = t.map((f, d) => {
|
|
const p = d / (t.length - 1) * n, $ = r - o - (f - a) / h * (r - o * 2);
|
|
return `${p.toFixed(1)},${$.toFixed(1)}`;
|
|
}).join(" "), u = `0,${r} ${c} ${n},${r}`;
|
|
return { linePoints: c, fillPoints: u };
|
|
}
|
|
function ni(t) {
|
|
return t === "light" ? {
|
|
line: "rgba(255, 255, 255, 0.25)",
|
|
fillTop: "rgba(255, 255, 255, 0.25)",
|
|
fillBottom: "rgba(255, 255, 255, 0.12)"
|
|
} : {
|
|
line: "rgba(0, 0, 0, 0.15)",
|
|
fillTop: "rgba(0, 0, 0, 0.2)",
|
|
fillBottom: "rgba(0, 0, 0, 0.1)"
|
|
};
|
|
}
|
|
let oi = 0;
|
|
function si(t) {
|
|
return t === "light" ? {
|
|
heating: "rgba(255, 255, 255, 0.4)",
|
|
cooling: "rgba(255, 255, 255, 0.4)"
|
|
} : {
|
|
heating: "rgba(0, 0, 0, 0.35)",
|
|
cooling: "rgba(0, 0, 0, 0.35)"
|
|
};
|
|
}
|
|
function ri(t, e) {
|
|
const n = e * 4, s = Array.from({ length: n }, () => null);
|
|
for (const o of t) {
|
|
if (o.action !== "heating" && o.action !== "cooling") continue;
|
|
const r = Math.floor(o.start * n), a = Math.ceil(o.end * n);
|
|
for (let l = r; l < a && l < n; l++)
|
|
l >= 0 && (o.action === "heating" || s[l] !== "heating") && (s[l] = o.action);
|
|
}
|
|
return s;
|
|
}
|
|
function ai(t) {
|
|
const { segments: e, data: i, width: n, height: s, colors: o, periodHours: r } = t;
|
|
if (i.length < 2) return D``;
|
|
const a = ri(e, r), l = a.length, h = t.min ?? Math.min(...i), u = (t.max ?? Math.max(...i)) - h || 1, f = 1, d = (m) => s - f - (m - h) / u * (s - f * 2), p = (m) => {
|
|
const g = m * (i.length - 1), b = Math.floor(g), w = Math.min(Math.ceil(g), i.length - 1), A = g - b;
|
|
return (i[b] ?? 0) * (1 - A) + (i[w] ?? 0) * A;
|
|
}, $ = [];
|
|
let v = null;
|
|
for (let m = 0; m < l; m++) {
|
|
const g = a[m];
|
|
g ? v !== null && v.action === g ? v.end = (m + 1) / l : (v && $.push(v), v = { start: m / l, end: (m + 1) / l, action: g }) : v && ($.push(v), v = null);
|
|
}
|
|
return v && $.push(v), D`
|
|
${$.map((m) => {
|
|
const g = m.action === "heating" ? o.heating : o.cooling, b = 10, w = (m.end - m.start) / b;
|
|
let A = `${(m.start * n).toFixed(1)},${s}`;
|
|
for (let k = 0; k <= b; k++) {
|
|
const C = m.start + k * w, y = C * n, z = d(p(C));
|
|
A += ` ${y.toFixed(1)},${z.toFixed(1)}`;
|
|
}
|
|
return A += ` ${(m.end * n).toFixed(1)},${s}`, D`<polygon points="${A}" fill="${g}" />`;
|
|
})}
|
|
`;
|
|
}
|
|
function Ne(t, e = {}, i) {
|
|
const { width: n = 100, height: s = 20, mode: o = "dark", line: r, fill: a, hvac: l, periodHours: h = 24 } = e, c = t.length > 1, u = l?.show !== !1 && i && i.length > 0, { linePoints: f, fillPoints: d } = c ? ii(t, e, 0) : { linePoints: "", fillPoints: "" }, p = ni(o), $ = si(o), v = a?.show !== !1 && c, m = r?.show !== !1 && c, g = `sparkline-gradient-${oi++}`, b = !!a?.style, w = a?.style || `fill: url(#${g});`, A = `stroke: ${p.line}; stroke-width: 1.5;`, k = r?.style ? `${A} ${r.style}` : A;
|
|
return D`
|
|
<svg viewBox="0 0 ${n} ${s}" preserveAspectRatio="none">
|
|
${v && !b ? D`
|
|
<defs>
|
|
<linearGradient id="${g}" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
<stop offset="0%" style="stop-color: ${p.fillTop};" />
|
|
<stop offset="100%" style="stop-color: ${p.fillBottom};" />
|
|
</linearGradient>
|
|
</defs>
|
|
` : _}
|
|
${u && c ? ai({ segments: i, data: t, width: n, height: s, colors: $, periodHours: h, min: e.min, max: e.max }) : _}
|
|
${v && !i ? D`<polygon points="${d}" style="${w}" />` : _}
|
|
${m ? D`<polyline points="${f}" style="fill: none; stroke-linecap: round; stroke-linejoin: round; ${k}" />` : _}
|
|
</svg>
|
|
`;
|
|
}
|
|
function li(t, e = {}) {
|
|
if (t && typeof t == "object" && "temperature" in t) {
|
|
const i = t, n = i.temperature && i.temperature.length > 1, s = i.hvacActions && i.hvacActions.length > 0;
|
|
return n || s ? Ne(
|
|
n ? i.temperature : [],
|
|
e,
|
|
i.hvacActions
|
|
) : _;
|
|
}
|
|
return Array.isArray(t) && t.length > 1 ? Ne(t, e) : _;
|
|
}
|
|
function ci(t) {
|
|
return t === "heating" || t === "cooling" || t === "idle" || t === "off";
|
|
}
|
|
const Xe = {
|
|
"12h": { hours: 12, statsPeriod: "5minute" },
|
|
"24h": { hours: 24, statsPeriod: "hour" },
|
|
"7d": { hours: 168, statsPeriod: "hour" },
|
|
"30d": { hours: 720, statsPeriod: "day" }
|
|
}, De = /* @__PURE__ */ new Map(), hi = 300 * 1e3;
|
|
async function ui(t, e, i = "24h") {
|
|
const n = /* @__PURE__ */ new Map(), s = Date.now(), o = [];
|
|
for (const r of e) {
|
|
const a = `${r}:${i}`, l = De.get(a);
|
|
l && s - l.fetchedAt < hi ? n.set(r, l.data) : o.push(r);
|
|
}
|
|
if (o.length > 0) {
|
|
const r = await di(t, o, i);
|
|
for (const [a, l] of r) {
|
|
const h = `${a}:${i}`;
|
|
De.set(h, { data: l, fetchedAt: s }), n.set(a, l);
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
async function di(t, e, i) {
|
|
const n = /* @__PURE__ */ new Map();
|
|
if (e.length === 0)
|
|
return n;
|
|
const s = e.filter((r) => r.startsWith("climate.")), o = e.filter((r) => !r.startsWith("climate."));
|
|
if (o.length > 0) {
|
|
const r = await Qe(t, o, i);
|
|
for (const [a, l] of r)
|
|
n.set(a, { temperature: l });
|
|
}
|
|
if (s.length > 0) {
|
|
const r = await fi(t, s, i);
|
|
for (const [a, l] of r)
|
|
n.set(a, l);
|
|
}
|
|
return n;
|
|
}
|
|
async function fi(t, e, i) {
|
|
const n = /* @__PURE__ */ new Map(), s = Xe[i], o = new Date(Date.now() - s.hours * 60 * 60 * 1e3), r = await Qe(t, e, i), a = [];
|
|
for (const h of e) {
|
|
const c = r.get(h);
|
|
!c || c.length === 0 ? a.push(h) : n.set(h, { temperature: c });
|
|
}
|
|
const l = await pi(t, e, o, s);
|
|
for (const h of a) {
|
|
const c = l.get(h);
|
|
c && c.temperature.length > 0 && n.set(h, { temperature: c.temperature });
|
|
}
|
|
for (const [h, c] of l) {
|
|
const u = n.get(h);
|
|
u ? u.hvacActions = c.hvacActions : n.set(h, { temperature: [], hvacActions: c.hvacActions });
|
|
}
|
|
return n;
|
|
}
|
|
async function pi(t, e, i, n) {
|
|
const s = /* @__PURE__ */ new Map();
|
|
if (e.length === 0)
|
|
return s;
|
|
try {
|
|
const o = await t.callWS({
|
|
type: "history/history_during_period",
|
|
start_time: i.toISOString(),
|
|
entity_ids: e,
|
|
no_attributes: !1,
|
|
significant_changes_only: !1
|
|
// Get all changes for better temperature resolution
|
|
});
|
|
if (o) {
|
|
const r = n.hours * 60 * 60 * 1e3, a = Date.now();
|
|
for (const l of e) {
|
|
const h = o[l];
|
|
if (h && h.length > 0) {
|
|
const c = gi(h), u = _i(h, i.getTime(), a, r);
|
|
s.set(l, {
|
|
temperature: c,
|
|
hvacActions: u
|
|
});
|
|
}
|
|
}
|
|
}
|
|
} catch (o) {
|
|
console.warn("[treemap] Failed to fetch climate history:", o);
|
|
}
|
|
return s;
|
|
}
|
|
function gi(t) {
|
|
const e = [], n = Math.max(1, Math.floor(t.length / 60));
|
|
for (let s = 0; s < t.length; s += n) {
|
|
const o = t[s];
|
|
if (!o) continue;
|
|
const a = (o.attributes || o.a)?.current_temperature;
|
|
typeof a == "number" && !Number.isNaN(a) && e.push(a);
|
|
}
|
|
return e;
|
|
}
|
|
function _i(t, e, i, n) {
|
|
const s = [];
|
|
for (let o = 0; o < t.length; o++) {
|
|
const r = t[o];
|
|
if (!r) continue;
|
|
const a = t[o + 1], h = (r.attributes || r.a)?.hvac_action;
|
|
if (!ci(h) || h === "idle" || h === "off") continue;
|
|
const c = h, u = r.last_updated ? new Date(r.last_updated).getTime() : new Date((r.lu ?? 0) * 1e3).getTime();
|
|
let f;
|
|
a ? f = a.last_updated ? new Date(a.last_updated).getTime() : new Date((a.lu ?? 0) * 1e3).getTime() : f = i;
|
|
const d = Math.max(0, (u - e) / n), p = Math.min(1, (f - e) / n);
|
|
d < 1 && p > 0 && s.push({ start: d, end: p, action: c });
|
|
}
|
|
return s;
|
|
}
|
|
async function Qe(t, e, i) {
|
|
const n = /* @__PURE__ */ new Map();
|
|
if (e.length === 0)
|
|
return n;
|
|
const s = Xe[i], o = new Date(Date.now() - s.hours * 60 * 60 * 1e3).toISOString();
|
|
try {
|
|
const r = await t.callWS({
|
|
type: "recorder/statistics_during_period",
|
|
start_time: o,
|
|
statistic_ids: e,
|
|
period: s.statsPeriod
|
|
});
|
|
if (r)
|
|
for (const [a, l] of Object.entries(r)) {
|
|
const h = l.map(({ mean: c }) => c).filter((c) => c != null);
|
|
n.set(a, h);
|
|
}
|
|
} catch (r) {
|
|
console.warn("[treemap] Failed to fetch statistics:", r);
|
|
for (const a of e)
|
|
n.has(a) || n.set(a, []);
|
|
}
|
|
return n;
|
|
}
|
|
function Oe(t, e) {
|
|
if (t.length === 0) return 1 / 0;
|
|
const i = t.reduce((a, l) => a + l, 0), n = Math.max(...t), s = Math.min(...t), o = e * e, r = i * i;
|
|
return Math.max(o * n / r, r / (o * s));
|
|
}
|
|
function mi(t, e, i) {
|
|
const n = t.reduce((r, a) => r + a.normalizedValue, 0), s = [];
|
|
let o = 0;
|
|
if (i) {
|
|
const r = n / e.height;
|
|
for (const a of t) {
|
|
const l = a.normalizedValue / r;
|
|
s.push({
|
|
label: a.item.label,
|
|
value: a.item.value,
|
|
sizeValue: a.item.sizeValue,
|
|
sortValue: a.item.sortValue,
|
|
colorValue: a.item.colorValue,
|
|
entity_id: a.item.entity_id,
|
|
icon: a.item.icon,
|
|
light: a.item.light,
|
|
climate: a.item.climate,
|
|
binary: a.item.binary,
|
|
color: a.item.color,
|
|
sparklineData: a.item.sparklineData,
|
|
unavailable: a.item.unavailable,
|
|
rawState: a.item.rawState,
|
|
x: e.x,
|
|
y: e.y + o,
|
|
width: r,
|
|
height: l
|
|
}), o += l;
|
|
}
|
|
return {
|
|
rects: s,
|
|
remaining: {
|
|
x: e.x + r,
|
|
y: e.y,
|
|
width: e.width - r,
|
|
height: e.height
|
|
}
|
|
};
|
|
} else {
|
|
const r = n / e.width;
|
|
for (const a of t) {
|
|
const l = a.normalizedValue / r;
|
|
s.push({
|
|
label: a.item.label,
|
|
value: a.item.value,
|
|
sizeValue: a.item.sizeValue,
|
|
sortValue: a.item.sortValue,
|
|
colorValue: a.item.colorValue,
|
|
entity_id: a.item.entity_id,
|
|
icon: a.item.icon,
|
|
light: a.item.light,
|
|
climate: a.item.climate,
|
|
binary: a.item.binary,
|
|
color: a.item.color,
|
|
sparklineData: a.item.sparklineData,
|
|
unavailable: a.item.unavailable,
|
|
rawState: a.item.rawState,
|
|
x: e.x + o,
|
|
y: e.y,
|
|
width: l,
|
|
height: r
|
|
}), o += l;
|
|
}
|
|
return {
|
|
rects: s,
|
|
remaining: {
|
|
x: e.x,
|
|
y: e.y + r,
|
|
width: e.width,
|
|
height: e.height - r
|
|
}
|
|
};
|
|
}
|
|
}
|
|
function bi(t, e, i) {
|
|
return e === "default" ? [...t] : [...t].sort((n, s) => {
|
|
let o;
|
|
if (e === "entity_id") {
|
|
const r = n.entity_id ?? "", a = s.entity_id ?? "";
|
|
o = r.localeCompare(a);
|
|
} else e === "label" ? o = n.label.localeCompare(s.label) : o = n.sortValue - s.sortValue;
|
|
return i ? o : -o;
|
|
});
|
|
}
|
|
function Ie(t, e, i, n = !1, s = "value") {
|
|
const o = t.length;
|
|
if (o === 0) return { rects: [], rows: 0 };
|
|
const r = e / i;
|
|
let a = Math.ceil(Math.sqrt(o * r)), l = Math.ceil(o / a);
|
|
for (; a * (l - 1) >= o && l > 1; ) l--;
|
|
for (; (a - 1) * l >= o && a > 1; ) a--;
|
|
const h = e / a, c = i / l;
|
|
return { rects: bi(t, s, n).map((d, p) => ({
|
|
label: d.label,
|
|
value: d.value,
|
|
sizeValue: d.sizeValue,
|
|
sortValue: d.sortValue,
|
|
colorValue: d.colorValue,
|
|
entity_id: d.entity_id,
|
|
icon: d.icon,
|
|
light: d.light,
|
|
climate: d.climate,
|
|
binary: d.binary,
|
|
color: d.color,
|
|
sparklineData: d.sparklineData,
|
|
unavailable: d.unavailable,
|
|
rawState: d.rawState,
|
|
x: p % a * h,
|
|
y: Math.floor(p / a) * c,
|
|
width: h,
|
|
height: c
|
|
})), rows: l };
|
|
}
|
|
function vi(t, e, i, n) {
|
|
const s = t.map((c) => Math.abs(c.value)), o = Math.max(...s), r = e ? s.map((c) => Math.sqrt(c / o) * o) : s, a = r.reduce((c, u) => c + u, 0), l = [];
|
|
for (const [c, u] of t.entries()) {
|
|
const f = s[c], d = r[c];
|
|
f !== void 0 && d !== void 0 && d > 0 && u && l.push({ item: u, absValue: f, sizeValue: d });
|
|
}
|
|
const h = l.map(({ item: c, sizeValue: u }) => ({
|
|
item: c,
|
|
normalizedValue: u / a * i
|
|
}));
|
|
return n === "default" || (n === "entity_id" ? h.sort((c, u) => {
|
|
const f = c.item.entity_id ?? "", d = u.item.entity_id ?? "";
|
|
return f.localeCompare(d);
|
|
}) : n === "label" ? h.sort((c, u) => c.item.label.localeCompare(u.item.label)) : h.sort((c, u) => u.normalizedValue - c.normalizedValue)), h;
|
|
}
|
|
function yi(t, e) {
|
|
const i = [];
|
|
let n = [], s = [...t];
|
|
for (; s.length > 0; ) {
|
|
const o = s[0];
|
|
if (!o) break;
|
|
const r = [...n, o.normalizedValue], a = Oe(n, e), l = Oe(r, e), h = s.length <= 3 && n.length > 0;
|
|
if (n.length > 0 && l > a && !h)
|
|
break;
|
|
i.push(o), n = r, s = s.slice(1);
|
|
}
|
|
return { row: i, rowValues: n, newRemaining: s };
|
|
}
|
|
function $i(t, e, i) {
|
|
for (const o of t)
|
|
o.x = e - o.x - o.width, o.y = i - o.y - o.height;
|
|
const n = Math.min(...t.map((o) => o.x)), s = Math.min(...t.map((o) => o.y));
|
|
if (n !== 0 || s !== 0)
|
|
for (const o of t)
|
|
o.x -= n, o.y -= s;
|
|
}
|
|
function xi(t, e, i, n = {}) {
|
|
const { compressRange: s = !0, equalSize: o = !1, ascending: r = !1, sortBy: a = "value" } = n;
|
|
if (t.length === 0) return { rects: [], rows: 0 };
|
|
if (o)
|
|
return Ie(t, e, i, r, a);
|
|
const l = t.map((g) => Math.abs(g.value)), h = Math.max(...l);
|
|
if (h === 0) return { rects: [], rows: 0 };
|
|
const c = l.filter((g) => g > 0);
|
|
if (c.length > 1) {
|
|
const g = Math.min(...c);
|
|
if ((h - g) / h < 0.01)
|
|
return Ie(t, e, i, r, a);
|
|
}
|
|
const u = e * i, f = vi(t, s, u, a), d = [];
|
|
let p = { x: 0, y: 0, width: e, height: i }, $ = [...f];
|
|
for (; $.length > 0; ) {
|
|
const g = p.width < p.height, b = g ? p.height : p.width, { row: w, newRemaining: A } = yi($, b);
|
|
$ = A;
|
|
const { rects: k, remaining: C } = mi(w, p, g);
|
|
if (d.push(...k), p = C, p.width <= 0 || p.height <= 0) break;
|
|
}
|
|
r && d.length > 0 && $i(d, e, i);
|
|
const m = new Set(d.map((g) => Math.round(g.y * 100) / 100)).size;
|
|
return { rects: d, rows: m };
|
|
}
|
|
function wi(t) {
|
|
let e = Number.POSITIVE_INFINITY, i = Number.NEGATIVE_INFINITY, n = Number.POSITIVE_INFINITY, s = Number.NEGATIVE_INFINITY;
|
|
for (const o of t)
|
|
o.colorValue < e && (e = o.colorValue), o.colorValue > i && (i = o.colorValue), o.sizeValue < n && (n = o.sizeValue), o.sizeValue > s && (s = o.sizeValue);
|
|
return { colorMin: e, colorMax: i, sizeMin: n, sizeMax: s };
|
|
}
|
|
function Ai(t, e, i) {
|
|
const n = i + e;
|
|
for (const r of t)
|
|
r.sizeValue = n - r.sizeValue, r.sortValue = -r.sortValue;
|
|
const s = n - e, o = s * 0.1;
|
|
for (const r of t)
|
|
r.sizeValue < o && (r.sizeValue = o);
|
|
return s;
|
|
}
|
|
function He(t, e, i) {
|
|
const n = e ? !i : i;
|
|
return [...t].sort(
|
|
(s, o) => n ? s.sizeValue - o.sizeValue : o.sizeValue - s.sizeValue
|
|
);
|
|
}
|
|
function ki(t, e) {
|
|
let i = 1;
|
|
for (const n of t)
|
|
e !== void 0 && n.sizeValue > e && (n.sizeValue = e), n.sizeValue > i && (i = n.sizeValue);
|
|
return i;
|
|
}
|
|
function Ci(t, e, i) {
|
|
const n = e ?? i * 0.15;
|
|
for (const s of t)
|
|
s.sizeValue < n && (s.sizeValue = n);
|
|
}
|
|
function Si(t, e = {}) {
|
|
const { inverse: i = !1, ascending: n = !1, limit: s, sizeMin: o, sizeMax: r, sortBy: a } = e;
|
|
if (t.length === 0)
|
|
return { items: [], colorMin: 0, colorMax: 0 };
|
|
const l = wi(t);
|
|
if (l.sizeMin < 0) {
|
|
const f = Math.abs(l.sizeMin);
|
|
for (const d of t)
|
|
d.sizeValue = d.sizeValue + f;
|
|
l.sizeMin = 0, l.sizeMax = l.sizeMax + f;
|
|
}
|
|
i && Ai(t, l.sizeMin, l.sizeMax);
|
|
const h = !a || a === "value";
|
|
let c;
|
|
if (h) {
|
|
const f = He(t, i, n);
|
|
c = s !== void 0 && s > 0 ? f.slice(0, s) : f;
|
|
} else if (s !== void 0 && s > 0) {
|
|
const f = He(t, i, n), d = new Set(f.slice(0, s).map((p) => p.entity_id ?? p.label));
|
|
c = t.filter((p) => d.has(p.entity_id ?? p.label));
|
|
} else
|
|
c = [...t];
|
|
const u = ki(c, r);
|
|
return Ci(c, o, u), { items: c, colorMin: l.colorMin, colorMax: l.colorMax };
|
|
}
|
|
const zi = [
|
|
{ threshold: 1e12, suffix: "T" },
|
|
{ threshold: 1e9, suffix: "B" },
|
|
{ threshold: 1e6, suffix: "M" },
|
|
{ threshold: 1e3, suffix: "k" }
|
|
];
|
|
function Mi(t, e = 1, i = !1) {
|
|
let n = t, s = "";
|
|
if (i) {
|
|
for (const o of zi)
|
|
if (Math.abs(t) >= o.threshold) {
|
|
n = t / o.threshold, s = o.suffix;
|
|
break;
|
|
}
|
|
}
|
|
return n.toFixed(e) + s;
|
|
}
|
|
function Vi(t, e) {
|
|
return t !== void 0 ? t : e !== void 0 ? e : 1;
|
|
}
|
|
const Ei = Fe`
|
|
:host {
|
|
display: block;
|
|
}
|
|
|
|
ha-card {
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.treemap-header {
|
|
padding: 8px 16px 0px;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
color: var(--primary-text-color);
|
|
}
|
|
|
|
.card-content {
|
|
flex: 1;
|
|
padding: 16px;
|
|
}
|
|
|
|
.treemap-container {
|
|
position: relative;
|
|
width: 100%;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.treemap-item {
|
|
position: absolute;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 8px;
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
transition: filter 0.2s ease;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.treemap-item:hover {
|
|
filter: brightness(1.1);
|
|
}
|
|
|
|
.treemap-icon {
|
|
--mdc-icon-size: 28px;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
/* Pulsing animation for active HVAC states (heating/cooling) */
|
|
@keyframes pulse {
|
|
0%,
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
|
|
.treemap-icon.hvac-active {
|
|
animation: pulse 2s ease-in-out infinite;
|
|
}
|
|
|
|
.treemap-label {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: white;
|
|
text-align: center;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
max-width: 100%;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.treemap-value {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: rgba(255, 255, 255, 0.85);
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.treemap-item.small .treemap-icon {
|
|
--mdc-icon-size: 20px;
|
|
}
|
|
|
|
.treemap-item.small .treemap-label {
|
|
font-size: 9px;
|
|
}
|
|
|
|
.treemap-item.small .treemap-value {
|
|
font-size: 12px;
|
|
}
|
|
|
|
.treemap-item.tiny .treemap-icon {
|
|
--mdc-icon-size: 16px;
|
|
}
|
|
|
|
.treemap-item.tiny .treemap-label {
|
|
font-size: 8px;
|
|
}
|
|
|
|
.treemap-item.tiny .treemap-value {
|
|
display: none;
|
|
}
|
|
|
|
.error {
|
|
color: var(--error-color, #db4437);
|
|
padding: 16px;
|
|
}
|
|
|
|
.empty {
|
|
color: var(--secondary-text-color, #727272);
|
|
padding: 16px;
|
|
text-align: center;
|
|
}
|
|
|
|
/* Sparkline styles */
|
|
.treemap-sparkline {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 20px;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.treemap-sparkline svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.treemap-item.small .treemap-sparkline {
|
|
height: 14px;
|
|
}
|
|
|
|
.treemap-item.tiny .treemap-sparkline {
|
|
display: none;
|
|
}
|
|
`;
|
|
var Ti = Object.defineProperty, Pi = Object.getOwnPropertyDescriptor, he = (t, e, i, n) => {
|
|
for (var s = n > 1 ? void 0 : n ? Pi(e, i) : e, o = t.length - 1, r; o >= 0; o--)
|
|
(r = t[o]) && (s = (n ? r(e, i, s) : r(s)) || s);
|
|
return n && s && Ti(e, i, s), s;
|
|
};
|
|
const Ni = "0.15.2";
|
|
console.info(
|
|
`%c TREEMAP-CARD %c v${Ni}`,
|
|
"color: white; background: #3498db; font-weight: bold;",
|
|
"color: #3498db; background: white; font-weight: bold;"
|
|
);
|
|
let T = class extends B {
|
|
constructor() {
|
|
super(...arguments), this._sparklineData = /* @__PURE__ */ new Map(), this._fetchingSparklines = !1, this._debouncedFetchSparklines = Mt(() => void this._fetchSparklineData(), 100), this._entityConfigMap = /* @__PURE__ */ new Map(), this._holdTimer = null, this._holdFired = !1;
|
|
}
|
|
/**
|
|
* Return the editor element for visual configuration
|
|
*/
|
|
static getConfigElement() {
|
|
return Promise.resolve().then(() => rn), document.createElement("treemap-card-editor");
|
|
}
|
|
/**
|
|
* Return stub config for card picker (finds temperature sensors for preview)
|
|
*/
|
|
static getStubConfig(t) {
|
|
const e = Object.keys(t.states).filter((i) => i.startsWith("sensor.") ? t.states[i]?.attributes?.device_class === "temperature" : !1).slice(0, 3);
|
|
return {
|
|
type: "custom:treemap-card",
|
|
entities: e.length > 0 ? e : ["sensor.*"]
|
|
};
|
|
}
|
|
/**
|
|
* Optimize re-renders: only update when relevant entity states change
|
|
*/
|
|
shouldUpdate(t) {
|
|
if (t.has("_config") || t.has("_sparklineData"))
|
|
return !0;
|
|
if (t.has("hass") && this.hass && this._config) {
|
|
const e = this._getRelevantStatesHash();
|
|
return e !== this._lastRelevantStates ? (this._lastRelevantStates = e, !0) : !1;
|
|
}
|
|
return !0;
|
|
}
|
|
/**
|
|
* Get a hash of relevant entity states for change detection
|
|
*/
|
|
_getRelevantStatesHash() {
|
|
if (!this.hass) return "";
|
|
const t = this._getConfiguredEntityIds(), e = [];
|
|
for (const i of t) {
|
|
const n = this.hass.states[i];
|
|
n && e.push(`${i}:${n.state}:${n.last_updated}`);
|
|
}
|
|
return e.join("|");
|
|
}
|
|
/**
|
|
* Get entity IDs from config (supports wildcards)
|
|
*/
|
|
_getConfiguredEntityIds() {
|
|
if (!this.hass || !this._config) return [];
|
|
if (this._config.entity)
|
|
return [this._config.entity];
|
|
if (this._config.entities) {
|
|
const t = Object.keys(this.hass.states), e = [];
|
|
for (const i of this._config.entities) {
|
|
const { entity: n } = this._normalizeEntity(i);
|
|
if (n.includes("*"))
|
|
for (const s of t)
|
|
ie(s, n) && e.push(s);
|
|
else
|
|
e.push(n);
|
|
}
|
|
return e;
|
|
}
|
|
return [];
|
|
}
|
|
setConfig(t) {
|
|
if (!t.entities && !t.entity)
|
|
throw new Error('Please define "entities" (list) or "entity" (single with JSON array)');
|
|
if (this._config = {
|
|
gap: 4,
|
|
// smaller gap
|
|
...t
|
|
}, this._entityConfigMap = /* @__PURE__ */ new Map(), t.entities)
|
|
for (const e of t.entities) {
|
|
const i = this._normalizeEntity(e);
|
|
(i.tap_action || i.hold_action || i.double_tap_action) && this._entityConfigMap.set(i.entity, i);
|
|
}
|
|
}
|
|
/**
|
|
* Normalize EntityInput to object format
|
|
* Converts "sensor.foo" to { entity: "sensor.foo" }
|
|
* Preserves existing { entity, name, icon } objects as-is
|
|
*/
|
|
_normalizeEntity(t) {
|
|
return typeof t == "string" ? { entity: t } : Ze(t) ? t : { entity: String(t) };
|
|
}
|
|
getCardSize() {
|
|
return 4;
|
|
}
|
|
updated() {
|
|
this._debouncedFetchSparklines();
|
|
}
|
|
async _fetchSparklineData() {
|
|
if (!this.hass || this._fetchingSparklines || this._config?.sparkline?.show === !1) return;
|
|
const e = this._resolveData().map(({ entity_id: i }) => i).filter((i) => !!i);
|
|
if (e.length !== 0) {
|
|
this._fetchingSparklines = !0;
|
|
try {
|
|
const i = this._config?.sparkline?.period || "24h", n = await ui(this.hass, e, i);
|
|
n.size > 0 && !this._sparklineDataEquals(n) && (this._sparklineData = n);
|
|
} finally {
|
|
this._fetchingSparklines = !1;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Compare sparkline data maps to avoid unnecessary re-renders.
|
|
*/
|
|
_sparklineDataEquals(t) {
|
|
if (this._sparklineData.size !== t.size) return !1;
|
|
for (const [e, i] of t) {
|
|
const n = this._sparklineData.get(e);
|
|
if (!n || n.temperature.length !== i.temperature.length) return !1;
|
|
const s = n.hvacActions?.length ?? 0, o = i.hvacActions?.length ?? 0;
|
|
if (s !== o) return !1;
|
|
}
|
|
return !0;
|
|
}
|
|
/**
|
|
* Get period hours from config for HVAC quantization.
|
|
*/
|
|
_getPeriodHours() {
|
|
const t = this._config?.sparkline?.period || "24h";
|
|
return {
|
|
"12h": 12,
|
|
"24h": 24,
|
|
"7d": 168,
|
|
"30d": 720
|
|
}[t] ?? 24;
|
|
}
|
|
_resolveData() {
|
|
if (!this.hass || !this._config) return [];
|
|
const t = this._lastRelevantStates;
|
|
if (this._cachedData && this._cachedDataHash === t)
|
|
return this._cachedData;
|
|
let e;
|
|
return this._config.entities ? e = this._resolveEntities(this._config.entities) : this._config.entity ? e = this._resolveJsonEntity(this._config.entity) : e = [], this._cachedData = e, this._cachedDataHash = t, e;
|
|
}
|
|
_isExcluded(t) {
|
|
const e = this._config?.exclude;
|
|
if (!e || e.length === 0) return !1;
|
|
for (const i of e)
|
|
if (ie(t, i))
|
|
return !0;
|
|
return !1;
|
|
}
|
|
_resolveEntities(t) {
|
|
if (!this.hass) return [];
|
|
const e = [], i = Object.keys(this.hass.states);
|
|
for (const n of t) {
|
|
const {
|
|
entity: s,
|
|
name: o,
|
|
icon: r,
|
|
color: a
|
|
} = this._normalizeEntity(n), l = i.filter(
|
|
(h) => ie(h, s) && !this._isExcluded(h)
|
|
);
|
|
for (const h of l) {
|
|
const c = this.hass.states[h];
|
|
if (!c) continue;
|
|
const u = this._config?.label?.attribute || "friendly_name", f = u === "entity_id" ? h : String(c.attributes[u] ?? h.split(".").pop() ?? h), d = o ?? f, p = r ?? L(c.attributes.icon);
|
|
if (Gt(h)) {
|
|
const g = Yt(c), { brightness: b } = g;
|
|
let w;
|
|
g.isOn ? w = 40 + b / 100 * 60 : w = 10, e.push({
|
|
label: d,
|
|
value: b,
|
|
// Display brightness as percentage
|
|
sizeValue: w,
|
|
sortValue: b,
|
|
colorValue: b,
|
|
entity_id: h,
|
|
icon: p ?? "mdi:lightbulb",
|
|
unit: "%",
|
|
light: g,
|
|
color: a
|
|
});
|
|
continue;
|
|
}
|
|
if (ei(h)) {
|
|
const g = ti(c), b = this._config?.value?.attribute || "current_temperature", w = this._config?.size?.attribute || b, A = this._config?.color?.attribute || b;
|
|
let k = fe(g, b), C = fe(g, w), y = fe(g, A);
|
|
k === null && (k = F(c.attributes[b]) ?? 0), C === null && (C = F(c.attributes[w]) ?? 0), y === null && (y = F(c.attributes[A]) ?? 0);
|
|
const z = typeof k == "number" ? k : 0, M = typeof C == "number" ? Math.max(0.1, Math.abs(C)) : 0.1, st = typeof y == "number" ? y : 0;
|
|
let R;
|
|
if (p)
|
|
R = p;
|
|
else
|
|
switch (g.hvacAction) {
|
|
case "heating": {
|
|
R = "mdi:fire";
|
|
break;
|
|
}
|
|
case "cooling": {
|
|
R = "mdi:snowflake";
|
|
break;
|
|
}
|
|
case "off": {
|
|
R = "mdi:thermostat-off";
|
|
break;
|
|
}
|
|
default:
|
|
R = "mdi:thermostat";
|
|
}
|
|
e.push({
|
|
label: d,
|
|
value: z,
|
|
sizeValue: M,
|
|
sortValue: z,
|
|
colorValue: st,
|
|
entity_id: h,
|
|
icon: R,
|
|
unit: L(c.attributes.unit_of_measurement),
|
|
climate: g,
|
|
color: a
|
|
});
|
|
continue;
|
|
}
|
|
if (Xt(h)) {
|
|
const g = Qt(c), { isOn: b } = g;
|
|
e.push({
|
|
label: d,
|
|
value: b ? 1 : 0,
|
|
sizeValue: 1,
|
|
// Equal size for all binary entities
|
|
sortValue: b ? 1 : 0,
|
|
colorValue: b ? 1 : 0,
|
|
entity_id: h,
|
|
icon: p,
|
|
binary: g,
|
|
color: a
|
|
});
|
|
continue;
|
|
}
|
|
const $ = this._config?.value?.attribute || "state";
|
|
let v;
|
|
if ($ === "state")
|
|
v = Number.parseFloat(c.state);
|
|
else {
|
|
const g = c.attributes[$];
|
|
v = Number.parseFloat(String(g ?? 0));
|
|
}
|
|
if (Number.isNaN(v)) {
|
|
if (!Lt(c.state) || !this._config?.filter?.unavailable)
|
|
continue;
|
|
const b = L(c.attributes.unit_of_measurement);
|
|
e.push({
|
|
label: d,
|
|
value: 0,
|
|
// Placeholder value for sizing
|
|
sizeValue: 1,
|
|
// Minimal size
|
|
sortValue: 0,
|
|
colorValue: 0,
|
|
entity_id: h,
|
|
icon: p,
|
|
unit: b,
|
|
unavailable: !0,
|
|
rawState: c.state,
|
|
color: a
|
|
});
|
|
continue;
|
|
}
|
|
const m = L(c.attributes.unit_of_measurement);
|
|
e.push({
|
|
label: d,
|
|
value: v,
|
|
sizeValue: v,
|
|
sortValue: v,
|
|
colorValue: v,
|
|
entity_id: h,
|
|
icon: p,
|
|
unit: m,
|
|
color: a
|
|
});
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
_resolveJsonEntity(t) {
|
|
if (!this.hass) return [];
|
|
const e = this.hass.states[t];
|
|
if (!e) return [];
|
|
const i = this._config?.data_attribute || "items", n = e.attributes[i];
|
|
if (!Array.isArray(n)) return [];
|
|
const s = this._config?.label?.attribute ?? this._config?.label?.param ?? "label", o = this._config?.value?.attribute ?? this._config?.value?.param ?? "value", r = this._config?.size?.attribute ?? this._config?.size?.param ?? o, a = this._config?.color?.attribute ?? this._config?.color?.param ?? o, l = this._config?.icon?.attribute ?? this._config?.icon?.param ?? "icon", h = this._config?.sparkline?.attribute;
|
|
return n.filter((c) => typeof c == "object" && c !== null).map((c) => {
|
|
let u;
|
|
if (h) {
|
|
const d = c[h];
|
|
Array.isArray(d) && (u = d.filter((p) => typeof p == "number"));
|
|
}
|
|
const f = Number(c[o] ?? 0);
|
|
return {
|
|
label: String(c[s] ?? c.label ?? ""),
|
|
value: f,
|
|
sizeValue: Number(c[r] ?? c[o] ?? 0),
|
|
sortValue: f,
|
|
colorValue: Number(c[a] ?? c[o] ?? 0),
|
|
icon: L(c[l]),
|
|
entity_id: L(c.entity_id),
|
|
sparklineData: u
|
|
};
|
|
}).filter((c) => c.label && !Number.isNaN(c.value));
|
|
}
|
|
_getGradientColorOptions() {
|
|
return {
|
|
colorHigh: this._config?.color?.high || "#16a34a",
|
|
colorLow: this._config?.color?.low || "#b91c1c",
|
|
colorMid: this._config?.color?.mid,
|
|
scaleMin: this._config?.color?.scale?.min,
|
|
scaleMax: this._config?.color?.scale?.max,
|
|
neutral: this._config?.color?.scale?.neutral,
|
|
opacity: this._config?.color?.opacity
|
|
};
|
|
}
|
|
_getColor(t, e, i) {
|
|
return Wt(t, e, i, this._getGradientColorOptions());
|
|
}
|
|
_getSizeClass(t) {
|
|
const e = t.width * t.height;
|
|
return e < 50 ? "tiny" : e < 150 ? "small" : "";
|
|
}
|
|
_getLightColor(t) {
|
|
const e = this._config?.color?.low ?? "#333333", i = this._config?.color?.high ?? "#fbbf24";
|
|
return Jt(t.light, e, i);
|
|
}
|
|
_filterData(t) {
|
|
if (!this._config) return t;
|
|
const e = this._config.filter?.above, i = this._config.filter?.below;
|
|
return t.filter((n) => n.unavailable ? !0 : !(e !== void 0 && n.value <= e || i !== void 0 && n.value >= i));
|
|
}
|
|
render() {
|
|
if (!this._config)
|
|
return x`<ha-card><div class="error">No configuration</div></ha-card>`;
|
|
const t = this._resolveData(), e = this._filterData(t), i = !!this._config.header?.title, n = this._config.header?.title, s = this._config.header?.show ?? !!n, o = this._config.header?.style || "", r = this._config.card_style || "", a = i ? void 0 : this._config.title;
|
|
if (e.length === 0)
|
|
return x`
|
|
<ha-card header="${a || _}" style="${r}">
|
|
${s && n ? x`<div class="treemap-header" style="${o}">${n}</div>` : _}
|
|
<div class="card-content">
|
|
<div class="empty">No data available</div>
|
|
</div>
|
|
</ha-card>
|
|
`;
|
|
const {
|
|
items: l,
|
|
colorMin: h,
|
|
colorMax: c
|
|
} = Si(e, {
|
|
inverse: this._config?.size?.inverse,
|
|
ascending: this._config?.order === "asc",
|
|
limit: this._config?.limit,
|
|
sizeMin: this._config?.size?.min,
|
|
sizeMax: this._config?.size?.max,
|
|
sortBy: this._config?.sort_by ?? "value"
|
|
}), u = this._config?.size?.equal === !0, f = /* @__PURE__ */ new Map();
|
|
for (const y of l) {
|
|
const z = y.entity_id ?? y.label;
|
|
f.set(z, {
|
|
value: y.value,
|
|
sortValue: y.sortValue,
|
|
colorValue: y.colorValue,
|
|
sizeValue: y.sizeValue,
|
|
unit: y.unit
|
|
});
|
|
}
|
|
const d = l.map((y) => ({ ...y, value: y.sizeValue })), p = this._config?.order === "asc", v = this._config?.size?.inverse === !0 ? !p : p, m = this._config?.sort_by ?? "value", { rects: g, rows: b } = xi(d, 100, 100, {
|
|
compressRange: !0,
|
|
equalSize: u,
|
|
ascending: v,
|
|
sortBy: m
|
|
});
|
|
for (const y of g) {
|
|
const z = y.entity_id ?? y.label, M = f.get(z);
|
|
M && (y.value = M.value, y.sortValue = M.sortValue, y.colorValue = M.colorValue, y.sizeValue = M.sizeValue, y.unit = M.unit);
|
|
}
|
|
const w = Math.max(1, b), A = Math.max(100, w * 100), k = this._config.height ?? A, C = this._config.gap ?? 6;
|
|
return x`
|
|
<ha-card header="${a || _}" style="${r}">
|
|
${s && n ? x`<div class="treemap-header" style="${o}">${n}</div>` : _}
|
|
<div class="card-content">
|
|
<div class="treemap-container" style="height: ${k}px">
|
|
${g.map((y) => this._renderRect(y, h, c, k, C))}
|
|
</div>
|
|
</div>
|
|
</ha-card>
|
|
`;
|
|
}
|
|
/**
|
|
* Determine color for a treemap rect based on entity type and state.
|
|
* Priority: unavailable > climate off > climate HVAC active > light > gradient
|
|
*/
|
|
_getRectColor(t, e, i) {
|
|
const n = this._config?.color?.opacity;
|
|
if (t.color)
|
|
return n === void 0 ? t.color : G(t.color, n);
|
|
if (t.unavailable) {
|
|
const s = this._config?.color?.unavailable ?? "#868e96";
|
|
return n === void 0 ? s : G(s, n);
|
|
}
|
|
if (t.climate && (t.climate.hvacMode === "off" || t.climate.hvacMode === "unavailable")) {
|
|
const s = this._config?.color?.hvac?.off ?? "#868e96";
|
|
return n === void 0 ? s : G(s, n);
|
|
}
|
|
if (t.climate && this._config?.color?.hvac) {
|
|
const s = this._config.color.hvac;
|
|
if (t.climate.hvacAction === "heating" && s.heating)
|
|
return n === void 0 ? s.heating : G(s.heating, n);
|
|
if (t.climate.hvacAction === "cooling" && s.cooling)
|
|
return n === void 0 ? s.cooling : G(s.cooling, n);
|
|
}
|
|
if (t.light)
|
|
return this._getLightColor(t);
|
|
if (t.binary) {
|
|
const s = this._config?.color?.low ?? "#333333", o = this._config?.color?.high ?? "#22c55e";
|
|
return Kt(t.binary.isOn, s, o);
|
|
}
|
|
return this._getColor(t.colorValue, e, i);
|
|
}
|
|
/**
|
|
* Format label with replace regex and prefix/suffix.
|
|
*/
|
|
_formatLabel(t) {
|
|
let e = t.label;
|
|
const i = this._config?.label?.replace;
|
|
if (i) {
|
|
const o = i.split("/"), r = o[0], a = o[1];
|
|
if (o.length >= 2 && r !== void 0 && a !== void 0)
|
|
try {
|
|
const l = new RegExp(r, o[2] ?? "");
|
|
e = t.label.replace(l, a);
|
|
} catch {
|
|
}
|
|
}
|
|
const n = this._config?.label?.prefix || "", s = this._config?.label?.suffix || "";
|
|
return `${n}${e}${s}`;
|
|
}
|
|
/**
|
|
* Format value with precision, abbreviation, prefix/suffix, and unit.
|
|
*/
|
|
_formatValue(t) {
|
|
const e = this._config?.value?.prefix, i = this._config?.value?.suffix;
|
|
if (t.unavailable && t.rawState)
|
|
return t.rawState.charAt(0).toUpperCase() + t.rawState.slice(1);
|
|
if (t.binary)
|
|
return t.binary.isOn ? "On" : "Off";
|
|
const n = t.entity_id ? this.hass?.entities?.[t.entity_id]?.display_precision : void 0, s = Vi(this._config?.value?.precision, n), o = this._config?.value?.abbreviate ?? !1, r = Mi(t.value, s, o), l = this._config?.value?.attribute === "temp_offset" && t.value > 0 ? "+" : "", h = e !== void 0 || i !== void 0, c = t.unit ? " " + t.unit : "";
|
|
return h ? `${e ?? ""}${l}${r}${i ?? ""}` : `${l}${r}${c}`;
|
|
}
|
|
/**
|
|
* Calculate styles for icon, label, and value based on color target mode.
|
|
*/
|
|
_calculateStyles(t) {
|
|
const i = (this._config?.color?.target ?? "background") === "foreground", n = i ? "rgba(0, 0, 0, 0.1)" : t, s = i ? { icon: t, label: t, value: t } : Ft(t), o = this._config?.icon?.style || `color: ${s.icon};`, r = this._config?.label?.style || `color: ${s.label};`, a = this._config?.value?.style || `color: ${s.value};`;
|
|
return { backgroundColor: n, iconStyle: o, labelStyle: r, valueStyle: a };
|
|
}
|
|
_renderRect(t, e, i, n, s) {
|
|
const o = this._getRectColor(t, e, i), r = this._getSizeClass(t), a = t.climate?.hvacAction === "heating" || t.climate?.hvacAction === "cooling", l = this._config?.icon?.show ?? !0, h = this._config?.label?.show ?? !0, c = this._config?.value?.show ?? !0, u = this._formatLabel(t), f = this._formatValue(t), { backgroundColor: d, iconStyle: p, labelStyle: $, valueStyle: v } = this._calculateStyles(o), m = s / 2;
|
|
return x`
|
|
<div
|
|
class="treemap-item ${r}"
|
|
style="
|
|
left: calc(${t.x}% + ${m}px);
|
|
top: calc(${t.y}% + ${m}px);
|
|
width: calc(${t.width}% - ${s}px);
|
|
height: calc(${t.height}% - ${s}px);
|
|
background-color: ${d};
|
|
"
|
|
@pointerdown="${(g) => this._onPointerDown(g, t)}"
|
|
@pointerup="${(g) => this._onPointerUp(g, t)}"
|
|
@pointercancel="${() => this._clearHoldTimer()}"
|
|
title="${t.label}: ${t.value}"
|
|
>
|
|
${l && (t.icon || this._config?.icon?.icon) ? x`<ha-icon
|
|
class="treemap-icon ${a ? "hvac-active" : ""}"
|
|
style="${p}"
|
|
icon="${t.icon || this._config?.icon?.icon}"
|
|
></ha-icon>` : _}
|
|
${h ? x`<span class="treemap-label" style="${$}">${u}</span>` : _}
|
|
${c ? x`<span class="treemap-value" style="${v}">${f}</span>` : _}
|
|
${this._config?.sparkline?.show !== !1 ? (() => {
|
|
const g = t.sparklineData ?? (t.entity_id ? this._sparklineData.get(t.entity_id) : void 0);
|
|
return x`<div class="treemap-sparkline">
|
|
${li(g, {
|
|
mode: this._config?.sparkline?.mode || "dark",
|
|
line: this._config?.sparkline?.line,
|
|
fill: this._config?.sparkline?.fill,
|
|
hvac: this._config?.sparkline?.hvac,
|
|
periodHours: this._getPeriodHours(),
|
|
min: this._config?.sparkline?.min,
|
|
max: this._config?.sparkline?.max
|
|
})}
|
|
</div>`;
|
|
})() : _}
|
|
</div>
|
|
`;
|
|
}
|
|
/**
|
|
* Get the effective action config for a rect, with per-entity override support.
|
|
*/
|
|
_getActionConfig(t, e) {
|
|
if (this._entityConfigMap.size > 0 && t.entity_id) {
|
|
const n = this._entityConfigMap.get(t.entity_id);
|
|
if (n?.[e]) return n[e];
|
|
for (const [s, o] of this._entityConfigMap)
|
|
if (o[e] && ie(t.entity_id, s))
|
|
return o[e];
|
|
}
|
|
const i = this._config?.[e];
|
|
return i || { action: e === "tap_action" ? "more-info" : "none" };
|
|
}
|
|
_executeAction(t, e) {
|
|
switch (t.action) {
|
|
case "more-info":
|
|
e && pe(this, "hass-more-info", { entityId: e });
|
|
break;
|
|
case "navigate":
|
|
"navigation_path" in t && t.navigation_path && Vt(this, t.navigation_path);
|
|
break;
|
|
case "url":
|
|
"url_path" in t && t.url_path && window.open(t.url_path);
|
|
break;
|
|
case "toggle":
|
|
if (e && this.hass) {
|
|
const i = e.split(".")[0] ?? "homeassistant";
|
|
this.hass.callService(i, "toggle", { entity_id: e });
|
|
}
|
|
break;
|
|
case "call-service":
|
|
if ("service" in t && t.service && this.hass) {
|
|
const i = t.service.split("."), n = i[0] ?? "", s = i[1] ?? "";
|
|
this.hass.callService(n, s, t.service_data);
|
|
}
|
|
break;
|
|
case "assist":
|
|
pe(this, "hass-launch-voice-assistant", {});
|
|
break;
|
|
}
|
|
}
|
|
_onPointerDown(t, e) {
|
|
if (t.button !== 0 && t.pointerType === "mouse") return;
|
|
this._holdFired = !1, this._clearHoldTimer();
|
|
const i = this._getActionConfig(e, "hold_action");
|
|
i.action !== "none" && (this._holdTimer = setTimeout(() => {
|
|
this._holdFired = !0, this._clearHoldTimer(), this._executeAction(i, e.entity_id);
|
|
}, T._HOLD_THRESHOLD_MS));
|
|
}
|
|
_onPointerUp(t, e) {
|
|
if (t.button !== 0 && t.pointerType === "mouse" || (this._clearHoldTimer(), this._holdFired)) return;
|
|
const i = this._getActionConfig(e, "tap_action");
|
|
i.action !== "none" && this._executeAction(i, e.entity_id);
|
|
}
|
|
_clearHoldTimer() {
|
|
this._holdTimer !== null && (clearTimeout(this._holdTimer), this._holdTimer = null);
|
|
}
|
|
};
|
|
T.styles = Ei;
|
|
T._HOLD_THRESHOLD_MS = 500;
|
|
he([
|
|
ce({ attribute: !1 })
|
|
], T.prototype, "hass", 2);
|
|
he([
|
|
ve()
|
|
], T.prototype, "_config", 2);
|
|
he([
|
|
ve()
|
|
], T.prototype, "_sparklineData", 2);
|
|
T = he([
|
|
Je("treemap-card")
|
|
], T);
|
|
window.customCards = window.customCards || [];
|
|
window.customCards.push({
|
|
type: "treemap-card",
|
|
name: "Treemap Card",
|
|
description: "Visualize entities as a treemap with size and color based on values",
|
|
preview: !0
|
|
});
|
|
function ae(t) {
|
|
return t === "__proto__";
|
|
}
|
|
function Di(t) {
|
|
switch (typeof t) {
|
|
case "number":
|
|
case "symbol":
|
|
return !1;
|
|
case "string":
|
|
return t.includes(".") || t.includes("[") || t.includes("]");
|
|
}
|
|
}
|
|
function $e(t) {
|
|
return typeof t == "string" || typeof t == "symbol" ? t : Object.is(t?.valueOf?.(), -0) ? "-0" : String(t);
|
|
}
|
|
function Ke(t) {
|
|
if (t == null)
|
|
return "";
|
|
if (typeof t == "string")
|
|
return t;
|
|
if (Array.isArray(t))
|
|
return t.map(Ke).join(",");
|
|
const e = String(t);
|
|
return e === "0" && Object.is(Number(t), -0) ? "-0" : e;
|
|
}
|
|
function et(t) {
|
|
if (Array.isArray(t))
|
|
return t.map($e);
|
|
if (typeof t == "symbol")
|
|
return [t];
|
|
t = Ke(t);
|
|
const e = [], i = t.length;
|
|
if (i === 0)
|
|
return e;
|
|
let n = 0, s = "", o = "", r = !1;
|
|
for (t.charCodeAt(0) === 46 && (e.push(""), n++); n < i; ) {
|
|
const a = t[n];
|
|
o ? a === "\\" && n + 1 < i ? (n++, s += t[n]) : a === o ? o = "" : s += a : r ? a === '"' || a === "'" ? o = a : a === "]" ? (r = !1, e.push(s), s = "") : s += a : a === "[" ? (r = !0, s && (e.push(s), s = "")) : a === "." ? s && (e.push(s), s = "") : s += a, n++;
|
|
}
|
|
return s && e.push(s), e;
|
|
}
|
|
function tt(t, e, i) {
|
|
if (t == null)
|
|
return i;
|
|
switch (typeof e) {
|
|
case "string": {
|
|
if (ae(e))
|
|
return i;
|
|
const n = t[e];
|
|
return n === void 0 ? Di(e) ? tt(t, et(e), i) : i : n;
|
|
}
|
|
case "number":
|
|
case "symbol": {
|
|
typeof e == "number" && (e = $e(e));
|
|
const n = t[e];
|
|
return n === void 0 ? i : n;
|
|
}
|
|
default: {
|
|
if (Array.isArray(e))
|
|
return Oi(t, e, i);
|
|
if (Object.is(e?.valueOf(), -0) ? e = "-0" : e = String(e), ae(e))
|
|
return i;
|
|
const n = t[e];
|
|
return n === void 0 ? i : n;
|
|
}
|
|
}
|
|
}
|
|
function Oi(t, e, i) {
|
|
if (e.length === 0)
|
|
return i;
|
|
let n = t;
|
|
for (let s = 0; s < e.length; s++) {
|
|
if (n == null || ae(e[s]))
|
|
return i;
|
|
n = n[e[s]];
|
|
}
|
|
return n === void 0 ? i : n;
|
|
}
|
|
function Re(t) {
|
|
return t !== null && (typeof t == "object" || typeof t == "function");
|
|
}
|
|
function Ii(t, e) {
|
|
return t === e || Number.isNaN(t) && Number.isNaN(e);
|
|
}
|
|
const Hi = /^(?:0|[1-9]\d*)$/;
|
|
function Ri(t, e = Number.MAX_SAFE_INTEGER) {
|
|
switch (typeof t) {
|
|
case "number":
|
|
return Number.isInteger(t) && t >= 0 && t < e;
|
|
case "symbol":
|
|
return !1;
|
|
case "string":
|
|
return Hi.test(t);
|
|
}
|
|
}
|
|
function Li(t) {
|
|
return typeof t == "symbol" || t instanceof Symbol;
|
|
}
|
|
const Ui = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, Bi = /^\w*$/;
|
|
function Fi(t, e) {
|
|
return Array.isArray(t) ? !1 : typeof t == "number" || typeof t == "boolean" || t == null || Li(t) ? !0 : typeof t == "string" && (Bi.test(t) || !Ui.test(t)) || e != null && Object.hasOwn(e, t);
|
|
}
|
|
const ji = (t, e, i) => {
|
|
const n = t[e];
|
|
(!(Object.hasOwn(t, e) && Ii(n, i)) || i === void 0 && !(e in t)) && (t[e] = i);
|
|
};
|
|
function qi(t, e, i, n) {
|
|
if (t == null && !Re(t))
|
|
return t;
|
|
let s;
|
|
Fi(e, t) ? s = [e] : Array.isArray(e) ? s = e : s = et(e);
|
|
const o = i(tt(t, s));
|
|
let r = t;
|
|
for (let a = 0; a < s.length && r != null; a++) {
|
|
const l = $e(s[a]);
|
|
if (ae(l))
|
|
continue;
|
|
let h;
|
|
if (a === s.length - 1)
|
|
h = o;
|
|
else {
|
|
const c = r[l], u = n?.(c, l, t);
|
|
h = u !== void 0 ? u : Re(c) ? c : Ri(s[a + 1]) ? [] : {};
|
|
}
|
|
ji(r, l, h), r = r[l];
|
|
}
|
|
return t;
|
|
}
|
|
function N(t, e, i) {
|
|
return qi(t, e, () => i, () => {
|
|
});
|
|
}
|
|
const Wi = Fe`
|
|
:host {
|
|
display: block;
|
|
}
|
|
|
|
.editor {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
/* Info message for YAML-only features */
|
|
.info-message {
|
|
padding: 12px 16px;
|
|
background: var(--secondary-background-color, #f5f5f5);
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
color: var(--secondary-text-color, #666);
|
|
}
|
|
|
|
/* Field styling */
|
|
.field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
.field-label {
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
color: var(--secondary-text-color, #666);
|
|
}
|
|
|
|
.field-helper {
|
|
font-size: 11px;
|
|
color: var(--secondary-text-color, #888);
|
|
}
|
|
|
|
/* Inputs */
|
|
.input {
|
|
width: 100%;
|
|
padding: 8px 12px;
|
|
border: 1px solid var(--divider-color, #e0e0e0);
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
background: var(--card-background-color, #fff);
|
|
color: var(--primary-text-color, #333);
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.input:focus {
|
|
outline: none;
|
|
border-color: var(--primary-color, #03a9f4);
|
|
}
|
|
|
|
.textarea {
|
|
width: 100%;
|
|
padding: 8px 12px;
|
|
border: 1px solid var(--divider-color, #e0e0e0);
|
|
border-radius: 4px;
|
|
font-family: monospace;
|
|
font-size: 13px;
|
|
background: var(--card-background-color, #fff);
|
|
color: var(--primary-text-color, #333);
|
|
box-sizing: border-box;
|
|
resize: vertical;
|
|
}
|
|
|
|
.textarea:focus {
|
|
outline: none;
|
|
border-color: var(--primary-color, #03a9f4);
|
|
}
|
|
|
|
/* ha-expansion-panel */
|
|
ha-expansion-panel input[type='checkbox'] {
|
|
width: 16px;
|
|
height: 16px;
|
|
margin: 0 8px 0 0;
|
|
cursor: pointer;
|
|
accent-color: var(--primary-color, #03a9f4);
|
|
}
|
|
|
|
ha-expansion-panel .content {
|
|
padding: 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
/* Docs link at end of section */
|
|
.docs-link {
|
|
align-self: flex-end;
|
|
color: var(--secondary-text-color, #888);
|
|
opacity: 0.6;
|
|
transition: opacity 0.2s;
|
|
padding: 4px 8px;
|
|
margin: -4px -8px;
|
|
min-width: 40px;
|
|
min-height: 28px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.docs-link:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.docs-link ha-icon {
|
|
--mdc-icon-size: 20px;
|
|
}
|
|
|
|
/* Color picker row */
|
|
.color-row {
|
|
display: flex;
|
|
gap: 16px;
|
|
}
|
|
|
|
.color-field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.color-field input[type='color'] {
|
|
width: 48px;
|
|
height: 32px;
|
|
padding: 0;
|
|
border: 1px solid var(--divider-color, #e0e0e0);
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
background: none;
|
|
}
|
|
|
|
.color-field input[type='color']::-webkit-color-swatch-wrapper {
|
|
padding: 2px;
|
|
}
|
|
|
|
.color-field input[type='color']::-webkit-color-swatch {
|
|
border: none;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.color-field span {
|
|
font-size: 11px;
|
|
color: var(--secondary-text-color, #666);
|
|
}
|
|
|
|
/* Inline input field */
|
|
.inline-field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
/* Side-by-side fields */
|
|
.field-row {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.field-row ha-textfield,
|
|
.field-row ha-select {
|
|
flex: 1;
|
|
}
|
|
|
|
/* Checkbox with label */
|
|
.checkbox-field {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
color: var(--primary-text-color, #333);
|
|
}
|
|
|
|
.checkbox-field input[type='checkbox'] {
|
|
width: 16px;
|
|
height: 16px;
|
|
margin: 0;
|
|
cursor: pointer;
|
|
accent-color: var(--primary-color, #03a9f4);
|
|
}
|
|
|
|
.checkbox-field input[type='checkbox']:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.checkbox-field input[type='checkbox']:disabled + span {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* Color + scale value row */
|
|
.color-scale-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.color-scale-row .color-field {
|
|
margin: 0;
|
|
}
|
|
|
|
.color-scale-row ha-textfield {
|
|
flex: 1;
|
|
}
|
|
|
|
.color-label {
|
|
font-size: 14px;
|
|
color: var(--primary-text-color, #333);
|
|
align-self: center;
|
|
}
|
|
|
|
/* Footer banner */
|
|
.footer-banner {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 8px;
|
|
font-size: 13px;
|
|
color: var(--secondary-text-color, #888);
|
|
}
|
|
|
|
.footer-banner a {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
color: var(--secondary-text-color, #888);
|
|
text-decoration: none;
|
|
transition: color 0.2s;
|
|
padding: 10px 12px;
|
|
min-height: 44px;
|
|
box-sizing: border-box;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.footer-banner a:hover {
|
|
color: var(--primary-text-color, #333);
|
|
background: var(--secondary-background-color, rgba(0, 0, 0, 0.05));
|
|
}
|
|
|
|
.footer-banner ha-icon {
|
|
--mdc-icon-size: 18px;
|
|
}
|
|
|
|
.footer-banner .separator {
|
|
opacity: 0.3;
|
|
}
|
|
`, it = { no_config: "Keine Konfiguration", yaml_only: "JSON-Attributmodus wird nur über YAML konfiguriert.", entities: { label: "Entitätsmuster (eines pro Zeile)", helper: "Wildcards (*) werden unterstützt", placeholder: `sensor.temperature_*
|
|
climate.*
|
|
light.living_*` }, exclude: { label: "Ausschlussmuster", placeholder: `*_battery
|
|
*_unavailable
|
|
light.*` }, label: { title: "Bezeichnung", prefix: "Präfix", suffix: "Suffix", find: "Suchen", replace: "Ersetzen mit", find_placeholder: "Temperatur", replace_placeholder: "Temp" }, value: { title: "Wert", prefix: "Präfix", suffix: "Suffix", suffix_placeholder: "%", precision: "Dezimalstellen", abbreviate: "Große Zahlen abkürzen (k, M, B)" }, icon: { title: "Symbol", override: "Symbol ersetzen" }, sparkline: { title: "Sparkline", period: "Zeitraum", period_12h: "12 Stunden", period_24h: "24 Stunden", period_7d: "7 Tage", period_30d: "30 Tage", mode: "Modus", mode_dark: "Dunkel", mode_light: "Hell", show_line: "Linie anzeigen", show_fill: "Füllung anzeigen", show_hvac: "HVAC-Balken anzeigen (Klimaentitäten)", min: "Y-Achse Min", max: "Y-Achse Max" }, colors: { title: "Farben", target: "Farbe anwenden auf", target_background: "Hintergrund", target_foreground: "Vordergrund (Text)", gradient: "Farbverlauf (niedrig → mittel → hoch)", low: "Niedrig ≤", mid: "Mittel =", high: "Hoch ≥", auto: "auto", middle: "mitte", hvac_colors: "HVAC-Farben (Klimaentitäten)", heating: "Heizen", cooling: "Kühlen", off: "Aus", unavailable: "Nicht verfügbar" }, size: { title: "Größe", equal: "Gleich große Rechtecke", inverse: "Invertiert (niedrige Werte = größer)", min: "Min", max: "Max" }, layout: { title: "Layout", height: "Höhe (px)", gap: "Abstand (px)" }, footer: { docs: "Doku", issues: "Bug oder Idee?", star: "Gefällt es? Stern geben!" }, actions: { title: "Aktionen", tap_action: "Tipp-Aktion", hold_action: "Halten-Aktion" }, data: { title: "Daten", order: "Sortierung", desc: "Absteigend (hoch zuerst)", asc: "Aufsteigend (niedrig zuerst)", limit: "Limit", all: "alle", above: "Filter oberhalb", below: "Filter unterhalb", unavailable: "Nicht verfügbare Entitäten einschließen", sort_by: "Sortieren nach", sort_by_value: "Wert (optimales Layout)", sort_by_entity_id: "Entitäts-ID", sort_by_label: "Bezeichnung", sort_by_default: "Konfigurationsreihenfolge" } }, Gi = {
|
|
editor: it
|
|
}, Yi = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
__proto__: null,
|
|
default: Gi,
|
|
editor: it
|
|
}, Symbol.toStringTag, { value: "Module" })), nt = { no_config: "No configuration", yaml_only: "JSON attribute mode is configured via YAML only.", entities: { label: "Entity patterns (one per line)", helper: "Wildcards (*) are supported", placeholder: `sensor.temperature_*
|
|
climate.*
|
|
light.living_*` }, exclude: { label: "Exclude patterns", placeholder: `*_battery
|
|
*_unavailable
|
|
light.*` }, label: { title: "Label", prefix: "Prefix", suffix: "Suffix", find: "Find", replace: "Replace with", find_placeholder: "Temperature", replace_placeholder: "Temp" }, value: { title: "Value", prefix: "Prefix", suffix: "Suffix", suffix_placeholder: "%", precision: "Decimal places", abbreviate: "Abbreviate large numbers (k, M, B)" }, icon: { title: "Icon", override: "Override icon" }, sparkline: { title: "Sparkline", period: "Period", period_12h: "12 hours", period_24h: "24 hours", period_7d: "7 days", period_30d: "30 days", mode: "Mode", mode_dark: "Dark", mode_light: "Light", show_line: "Show line", show_fill: "Show fill", show_hvac: "Show HVAC bars (climate entities)", min: "Y-axis min", max: "Y-axis max" }, colors: { title: "Colors", target: "Apply color to", target_background: "Background", target_foreground: "Foreground (text)", gradient: "Color gradient (low → mid → high)", low: "Low ≤", mid: "Mid =", high: "High ≥", auto: "auto", middle: "middle", hvac_colors: "HVAC colors (climate entities)", heating: "Heating", cooling: "Cooling", off: "Off", unavailable: "Unavailable" }, size: { title: "Size", equal: "Equal size rectangles", inverse: "Inverse (low values = bigger)", min: "Min", max: "Max" }, layout: { title: "Layout", height: "Height (px)", gap: "Gap (px)" }, footer: { docs: "Docs", issues: "Bug or idea?", star: "Like it? Star it!" }, actions: { title: "Actions", tap_action: "Tap action", hold_action: "Hold action" }, data: { title: "Data", order: "Order", desc: "Descending (high first)", asc: "Ascending (low first)", sort_by: "Sort by", sort_by_value: "Value (optimal layout)", sort_by_entity_id: "Entity ID", sort_by_label: "Label", sort_by_default: "Config order", limit: "Limit", all: "all", above: "Filter above", below: "Filter below", unavailable: "Include unavailable entities" } }, Ji = {
|
|
editor: nt
|
|
}, Zi = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
__proto__: null,
|
|
default: Ji,
|
|
editor: nt
|
|
}, Symbol.toStringTag, { value: "Module" })), ot = { no_config: "Aucune configuration", yaml_only: "Le mode attribut JSON est configurable uniquement via YAML.", entities: { label: "Motifs d'entités (un par ligne)", helper: "Wildcards (*) supportés", placeholder: `sensor.temperature_*
|
|
climate.*
|
|
light.living_*` }, exclude: { label: "Motifs à exclure", placeholder: `*_battery
|
|
*_unavailable
|
|
light.*` }, label: { title: "Libellé", prefix: "Préfixe", suffix: "Suffixe", find: "Rechercher", replace: "Remplacer par", find_placeholder: "Température", replace_placeholder: "Temp" }, value: { title: "Valeur", prefix: "Préfixe", suffix: "Suffixe", suffix_placeholder: "%", precision: "Décimales", abbreviate: "Abréger les grands nombres (k, M, B)" }, icon: { title: "Icône", override: "Remplacer l'icône" }, sparkline: { title: "Sparkline", period: "Période", period_12h: "12 heures", period_24h: "24 heures", period_7d: "7 jours", period_30d: "30 jours", mode: "Mode", mode_dark: "Sombre", mode_light: "Clair", show_line: "Afficher la ligne", show_fill: "Afficher le remplissage", show_hvac: "Afficher les barres HVAC (entités climatiques)", min: "Min axe Y", max: "Max axe Y" }, colors: { title: "Couleurs", target: "Appliquer la couleur à", target_background: "Arrière-plan", target_foreground: "Premier plan (texte)", gradient: "Dégradé de couleur (bas → milieu → haut)", low: "Bas ≤", mid: "Milieu =", high: "Haut ≥", auto: "auto", middle: "milieu", hvac_colors: "Couleurs HVAC (entités climatiques)", heating: "Chauffage", cooling: "Climatisation", off: "Éteint", unavailable: "Indisponible" }, size: { title: "Taille", equal: "Rectangles de taille égale", inverse: "Inverse (valeurs basses = plus grand)", min: "Min", max: "Max" }, layout: { title: "Disposition", height: "Hauteur (px)", gap: "Espacement (px)" }, footer: { docs: "Docs", issues: "Bug ou idée?", star: "Vous aimez? Étoile!" }, actions: { title: "Actions", tap_action: "Action au tap", hold_action: "Action au maintien" }, data: { title: "Données", order: "Ordre", desc: "Décroissant (haut en premier)", asc: "Croissant (bas en premier)", limit: "Limite", all: "tous", above: "Filtrer au-dessus", below: "Filtrer en-dessous", unavailable: "Inclure les entités indisponibles", sort_by: "Trier par", sort_by_value: "Valeur (disposition optimale)", sort_by_entity_id: "ID d'entité", sort_by_label: "Étiquette", sort_by_default: "Ordre de configuration" } }, Xi = {
|
|
editor: ot
|
|
}, Qi = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
__proto__: null,
|
|
default: Xi,
|
|
editor: ot
|
|
}, Symbol.toStringTag, { value: "Module" })), Le = {
|
|
de: Yi,
|
|
en: Zi,
|
|
fr: Qi
|
|
};
|
|
function Ki(t) {
|
|
return typeof t == "object" && t !== null;
|
|
}
|
|
function Ue(t, e) {
|
|
const i = e.split(".");
|
|
let n = t;
|
|
for (const s of i)
|
|
if (n && Ki(n) && s in n)
|
|
n = n[s];
|
|
else
|
|
return;
|
|
return typeof n == "string" ? n : void 0;
|
|
}
|
|
function en(t) {
|
|
return (t?.language ?? "en").split("-")[0] ?? "en";
|
|
}
|
|
function tn(t, e) {
|
|
const i = en(t), n = Le[i];
|
|
if (n) {
|
|
const s = Ue(n, e);
|
|
if (s) return s;
|
|
}
|
|
if (i !== "en") {
|
|
const s = Le.en;
|
|
if (s) {
|
|
const o = Ue(s, e);
|
|
if (o) return o;
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
var nn = Object.defineProperty, on = Object.getOwnPropertyDescriptor, ue = (t, e, i, n) => {
|
|
for (var s = n > 1 ? void 0 : n ? on(e, i) : e, o = t.length - 1, r; o >= 0; o--)
|
|
(r = t[o]) && (s = (n ? r(e, i, s) : r(s)) || s);
|
|
return n && s && nn(e, i, s), s;
|
|
};
|
|
const ne = "https://github.com/omachala/ha-treemap-card";
|
|
function Y(t) {
|
|
const e = t.target;
|
|
if (e && typeof e == "object" && "value" in e) {
|
|
const i = e.value;
|
|
return String(i ?? "");
|
|
}
|
|
return "";
|
|
}
|
|
function sn(t) {
|
|
const e = t.target;
|
|
return e instanceof HTMLInputElement ? e.checked : !1;
|
|
}
|
|
let H = class extends B {
|
|
constructor() {
|
|
super(...arguments), this._actionsSchema = [
|
|
{ name: "tap_action", selector: { ui_action: { default_action: "more-info" } } },
|
|
{ name: "hold_action", selector: { ui_action: { default_action: "none" } } }
|
|
];
|
|
}
|
|
setConfig(t) {
|
|
this._config = { ...t };
|
|
}
|
|
/**
|
|
* Shorthand for localize with current hass context
|
|
*/
|
|
_t(t) {
|
|
return tn(this.hass, t);
|
|
}
|
|
_fireConfigChanged() {
|
|
this._config && this.dispatchEvent(
|
|
new CustomEvent("config-changed", {
|
|
detail: { config: this._config },
|
|
bubbles: !0,
|
|
composed: !0
|
|
})
|
|
);
|
|
}
|
|
/**
|
|
* Generic handler for text input changes
|
|
*/
|
|
_handleTextChange(t, e) {
|
|
if (!this._config) return;
|
|
const i = Y(e) || void 0;
|
|
this._config = N({ ...this._config }, t, i), this._fireConfigChanged();
|
|
}
|
|
/**
|
|
* Generic handler for checkbox changes
|
|
*/
|
|
_handleBoolChange(t, e) {
|
|
if (!this._config) return;
|
|
const i = sn(e);
|
|
this._config = N({ ...this._config }, t, i), this._fireConfigChanged();
|
|
}
|
|
/**
|
|
* Generic handler for number input changes
|
|
*/
|
|
_handleNumberChange(t, e) {
|
|
if (!this._config) return;
|
|
const i = Y(e), n = i ? Number.parseFloat(i) : void 0;
|
|
this._config = N({ ...this._config }, t, n), this._fireConfigChanged();
|
|
}
|
|
/**
|
|
* Handler for entities textarea (splits lines into array)
|
|
*/
|
|
_handleEntitiesChange(t) {
|
|
if (!this._config) return;
|
|
const i = Y(t).split(`
|
|
`).map((n) => n.trim()).filter((n) => n.length > 0);
|
|
this._config = N({ ...this._config }, "entities", i), this._fireConfigChanged();
|
|
}
|
|
/**
|
|
* Handler for ha-icon-picker value-changed event
|
|
*/
|
|
_handleIconChange(t) {
|
|
if (!this._config) return;
|
|
const e = t.detail?.value || void 0;
|
|
this._config = N({ ...this._config }, "icon.icon", e), this._fireConfigChanged();
|
|
}
|
|
/**
|
|
* Handler for find/replace inputs (combines into 'find/replace' format)
|
|
*/
|
|
_handleReplaceChange(t, e) {
|
|
if (!this._config) return;
|
|
const i = Y(e), s = (this._config.label?.replace ?? "/").split("/"), o = t === "find" ? i : s[0] ?? "", r = t === "replace" ? i : s[1] ?? "", a = o || r ? `${o}/${r}` : void 0;
|
|
this._config = N({ ...this._config }, "label.replace", a), this._fireConfigChanged();
|
|
}
|
|
/**
|
|
* Handler for exclude textarea (splits lines into array, removes if empty)
|
|
*/
|
|
_handleExcludeChange(t) {
|
|
if (!this._config) return;
|
|
const i = Y(t).split(`
|
|
`).map((n) => n.trim()).filter((n) => n.length > 0);
|
|
this._config = N({ ...this._config }, "exclude", i.length > 0 ? i : void 0), this._fireConfigChanged();
|
|
}
|
|
_handleActionsChanged(t) {
|
|
if (!this._config) return;
|
|
const e = t.detail?.value?.tap_action, i = t.detail?.value?.hold_action;
|
|
this._config = {
|
|
...this._config,
|
|
...e !== void 0 && { tap_action: e },
|
|
...i !== void 0 && { hold_action: i }
|
|
}, this._fireConfigChanged();
|
|
}
|
|
_docsUrl(t) {
|
|
return `${ne}?tab=readme-ov-file#${t}`;
|
|
}
|
|
_renderDocsLink(t) {
|
|
return x`
|
|
<a class="docs-link" href=${this._docsUrl(t)} target="_blank" rel="noopener">
|
|
<ha-icon icon="mdi:help-circle-outline"></ha-icon>
|
|
</a>
|
|
`;
|
|
}
|
|
_renderToggleSection(t) {
|
|
return x`
|
|
<ha-expansion-panel outlined data-testid="${t.id}-section">
|
|
<input
|
|
slot="leading-icon"
|
|
type="checkbox"
|
|
.checked=${t.checked}
|
|
@change=${t.onToggle}
|
|
@click=${(e) => e.stopPropagation()}
|
|
/>
|
|
<span slot="header">${t.title}</span>
|
|
<div class="content">
|
|
${t.content} ${t.docsAnchor ? this._renderDocsLink(t.docsAnchor) : ""}
|
|
</div>
|
|
</ha-expansion-panel>
|
|
`;
|
|
}
|
|
render() {
|
|
if (!this._config)
|
|
return x`<div class="editor">${this._t("editor.no_config")}</div>`;
|
|
if (this._config.entity)
|
|
return x`
|
|
<div class="editor">
|
|
<ha-alert alert-type="info">${this._t("editor.yaml_only")}</ha-alert>
|
|
</div>
|
|
`;
|
|
const t = this._config.entities?.map((o) => Ze(o) ? o.entity : o).join(`
|
|
`) ?? "", e = this._config.exclude?.join(`
|
|
`) ?? "", i = this._config.label ?? {}, n = this._config.value ?? {}, s = this._config.icon ?? {};
|
|
return x`
|
|
<div class="editor">
|
|
<!-- Entities -->
|
|
<div class="field" data-testid="entities-field">
|
|
<label class="field-label">${this._t("editor.entities.label")}</label>
|
|
<textarea
|
|
class="textarea"
|
|
.value=${t}
|
|
@input=${this._handleEntitiesChange}
|
|
placeholder="sensor.temperature_* climate.* light.living_*"
|
|
rows="3"
|
|
></textarea>
|
|
<span class="field-helper">${this._t("editor.entities.helper")}</span>
|
|
</div>
|
|
|
|
<!-- Exclude -->
|
|
<div class="field" data-testid="exclude-field">
|
|
<label class="field-label">${this._t("editor.exclude.label")}</label>
|
|
<textarea
|
|
class="textarea"
|
|
.value=${e}
|
|
@input=${this._handleExcludeChange}
|
|
rows="2"
|
|
></textarea>
|
|
</div>
|
|
|
|
<!-- Label section -->
|
|
${this._renderToggleSection({
|
|
id: "label",
|
|
title: this._t("editor.label.title"),
|
|
checked: i.show !== !1,
|
|
onToggle: (o) => this._handleBoolChange("label.show", o),
|
|
docsAnchor: "label",
|
|
content: x`
|
|
<div class="field-row">
|
|
<ha-textfield
|
|
label=${this._t("editor.label.prefix")}
|
|
.value=${i.prefix ?? ""}
|
|
@input=${(o) => this._handleTextChange("label.prefix", o)}
|
|
></ha-textfield>
|
|
<ha-textfield
|
|
label=${this._t("editor.label.suffix")}
|
|
.value=${i.suffix ?? ""}
|
|
@input=${(o) => this._handleTextChange("label.suffix", o)}
|
|
></ha-textfield>
|
|
</div>
|
|
<div class="field-row">
|
|
<ha-textfield
|
|
label=${this._t("editor.label.find")}
|
|
.value=${i.replace?.split("/")[0] ?? ""}
|
|
@input=${(o) => this._handleReplaceChange("find", o)}
|
|
placeholder=${this._t("editor.label.find_placeholder")}
|
|
></ha-textfield>
|
|
<ha-textfield
|
|
label=${this._t("editor.label.replace")}
|
|
.value=${i.replace?.split("/")[1] ?? ""}
|
|
@input=${(o) => this._handleReplaceChange("replace", o)}
|
|
placeholder=${this._t("editor.label.replace_placeholder")}
|
|
></ha-textfield>
|
|
</div>
|
|
`
|
|
})}
|
|
|
|
<!-- Value section -->
|
|
${this._renderToggleSection({
|
|
id: "value",
|
|
title: this._t("editor.value.title"),
|
|
checked: n.show !== !1,
|
|
onToggle: (o) => this._handleBoolChange("value.show", o),
|
|
docsAnchor: "value",
|
|
content: x`
|
|
<div class="field-row">
|
|
<ha-textfield
|
|
label=${this._t("editor.value.prefix")}
|
|
.value=${n.prefix ?? ""}
|
|
@input=${(o) => this._handleTextChange("value.prefix", o)}
|
|
></ha-textfield>
|
|
<ha-textfield
|
|
label=${this._t("editor.value.suffix")}
|
|
.value=${n.suffix ?? ""}
|
|
@input=${(o) => this._handleTextChange("value.suffix", o)}
|
|
placeholder=${this._t("editor.value.suffix_placeholder")}
|
|
></ha-textfield>
|
|
</div>
|
|
<div class="field-row">
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.value.precision")}
|
|
.value=${n.precision ?? ""}
|
|
@input=${(o) => this._handleNumberChange("value.precision", o)}
|
|
placeholder="1"
|
|
></ha-textfield>
|
|
</div>
|
|
<label class="checkbox-field">
|
|
<input
|
|
type="checkbox"
|
|
.checked=${n.abbreviate ?? !1}
|
|
@change=${(o) => this._handleBoolChange("value.abbreviate", o)}
|
|
/>
|
|
<span>${this._t("editor.value.abbreviate")}</span>
|
|
</label>
|
|
`
|
|
})}
|
|
|
|
<!-- Icon section -->
|
|
${this._renderToggleSection({
|
|
id: "icon",
|
|
title: this._t("editor.icon.title"),
|
|
checked: s.show !== !1,
|
|
onToggle: (o) => this._handleBoolChange("icon.show", o),
|
|
docsAnchor: "icon",
|
|
content: x`
|
|
<ha-icon-picker
|
|
label=${this._t("editor.icon.override")}
|
|
.hass=${this.hass}
|
|
.value=${s.icon ?? ""}
|
|
@value-changed=${this._handleIconChange}
|
|
></ha-icon-picker>
|
|
`
|
|
})}
|
|
|
|
<!-- Sparkline section -->
|
|
<ha-expansion-panel outlined data-testid="sparkline-section">
|
|
<input
|
|
slot="leading-icon"
|
|
type="checkbox"
|
|
.checked=${this._config.sparkline?.show !== !1}
|
|
@change=${(o) => this._handleBoolChange("sparkline.show", o)}
|
|
@click=${(o) => o.stopPropagation()}
|
|
/>
|
|
<span slot="header">${this._t("editor.sparkline.title")}</span>
|
|
<div class="content">
|
|
<ha-select
|
|
label=${this._t("editor.sparkline.period")}
|
|
.value=${this._config.sparkline?.period ?? "24h"}
|
|
@selected=${(o) => this._handleTextChange("sparkline.period", o)}
|
|
@closed=${(o) => o.stopPropagation()}
|
|
>
|
|
<ha-list-item value="12h">${this._t("editor.sparkline.period_12h")}</ha-list-item>
|
|
<ha-list-item value="24h">${this._t("editor.sparkline.period_24h")}</ha-list-item>
|
|
<ha-list-item value="7d">${this._t("editor.sparkline.period_7d")}</ha-list-item>
|
|
<ha-list-item value="30d">${this._t("editor.sparkline.period_30d")}</ha-list-item>
|
|
</ha-select>
|
|
<ha-select
|
|
label=${this._t("editor.sparkline.mode")}
|
|
.value=${this._config.sparkline?.mode ?? "dark"}
|
|
@selected=${(o) => this._handleTextChange("sparkline.mode", o)}
|
|
@closed=${(o) => o.stopPropagation()}
|
|
>
|
|
<ha-list-item value="dark">${this._t("editor.sparkline.mode_dark")}</ha-list-item>
|
|
<ha-list-item value="light">${this._t("editor.sparkline.mode_light")}</ha-list-item>
|
|
</ha-select>
|
|
<label class="checkbox-field">
|
|
<input
|
|
type="checkbox"
|
|
.checked=${this._config.sparkline?.line?.show !== !1}
|
|
@change=${(o) => this._handleBoolChange("sparkline.line.show", o)}
|
|
/>
|
|
<span>${this._t("editor.sparkline.show_line")}</span>
|
|
</label>
|
|
<label class="checkbox-field">
|
|
<input
|
|
type="checkbox"
|
|
.checked=${this._config.sparkline?.fill?.show !== !1}
|
|
@change=${(o) => this._handleBoolChange("sparkline.fill.show", o)}
|
|
/>
|
|
<span>${this._t("editor.sparkline.show_fill")}</span>
|
|
</label>
|
|
<label class="checkbox-field">
|
|
<input
|
|
type="checkbox"
|
|
.checked=${this._config.sparkline?.hvac?.show !== !1}
|
|
@change=${(o) => this._handleBoolChange("sparkline.hvac.show", o)}
|
|
/>
|
|
<span>${this._t("editor.sparkline.show_hvac")}</span>
|
|
</label>
|
|
<div class="field-row">
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.sparkline.min")}
|
|
.value=${this._config.sparkline?.min ?? ""}
|
|
@input=${(o) => this._handleNumberChange("sparkline.min", o)}
|
|
placeholder=${this._t("editor.colors.auto")}
|
|
></ha-textfield>
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.sparkline.max")}
|
|
.value=${this._config.sparkline?.max ?? ""}
|
|
@input=${(o) => this._handleNumberChange("sparkline.max", o)}
|
|
placeholder=${this._t("editor.colors.auto")}
|
|
></ha-textfield>
|
|
</div>
|
|
${this._renderDocsLink("sparkline")}
|
|
</div>
|
|
</ha-expansion-panel>
|
|
|
|
<!-- Colors section -->
|
|
<ha-expansion-panel outlined data-testid="colors-section">
|
|
<span slot="header">${this._t("editor.colors.title")}</span>
|
|
<div class="content">
|
|
<ha-select
|
|
label=${this._t("editor.colors.target")}
|
|
.value=${this._config.color?.target ?? "background"}
|
|
@selected=${(o) => this._handleTextChange("color.target", o)}
|
|
@closed=${(o) => o.stopPropagation()}
|
|
>
|
|
<ha-list-item value="background"
|
|
>${this._t("editor.colors.target_background")}</ha-list-item
|
|
>
|
|
<ha-list-item value="foreground"
|
|
>${this._t("editor.colors.target_foreground")}</ha-list-item
|
|
>
|
|
</ha-select>
|
|
<span class="field-label">${this._t("editor.colors.gradient")}</span>
|
|
<div class="color-scale-row">
|
|
<label class="color-field">
|
|
<input
|
|
type="color"
|
|
.value=${this._config.color?.low ?? "#b91c1c"}
|
|
@input=${(o) => this._handleTextChange("color.low", o)}
|
|
/>
|
|
</label>
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.colors.low")}
|
|
.value=${this._config.color?.scale?.min ?? ""}
|
|
@input=${(o) => this._handleNumberChange("color.scale.min", o)}
|
|
placeholder=${this._t("editor.colors.auto")}
|
|
></ha-textfield>
|
|
</div>
|
|
<div class="color-scale-row">
|
|
<label class="color-field">
|
|
<input
|
|
type="color"
|
|
.value=${this._config.color?.mid ?? "#888888"}
|
|
@input=${(o) => this._handleTextChange("color.mid", o)}
|
|
/>
|
|
</label>
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.colors.mid")}
|
|
.value=${this._config.color?.scale?.neutral ?? ""}
|
|
@input=${(o) => this._handleNumberChange("color.scale.neutral", o)}
|
|
placeholder=${this._t("editor.colors.middle")}
|
|
></ha-textfield>
|
|
</div>
|
|
<div class="color-scale-row">
|
|
<label class="color-field">
|
|
<input
|
|
type="color"
|
|
.value=${this._config.color?.high ?? "#16a34a"}
|
|
@input=${(o) => this._handleTextChange("color.high", o)}
|
|
/>
|
|
</label>
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.colors.high")}
|
|
.value=${this._config.color?.scale?.max ?? ""}
|
|
@input=${(o) => this._handleNumberChange("color.scale.max", o)}
|
|
placeholder=${this._t("editor.colors.auto")}
|
|
></ha-textfield>
|
|
</div>
|
|
<div class="color-scale-row">
|
|
<label class="color-field">
|
|
<input
|
|
type="color"
|
|
.value=${this._config.color?.unavailable ?? "#868e96"}
|
|
@input=${(o) => this._handleTextChange("color.unavailable", o)}
|
|
/>
|
|
</label>
|
|
<span class="color-label">${this._t("editor.colors.unavailable")}</span>
|
|
</div>
|
|
<span class="field-label">${this._t("editor.colors.hvac_colors")}</span>
|
|
<div class="color-row">
|
|
<label class="color-field">
|
|
<input
|
|
type="color"
|
|
.value=${this._config.color?.hvac?.heating ?? "#ff6b35"}
|
|
@input=${(o) => this._handleTextChange("color.hvac.heating", o)}
|
|
/>
|
|
<span>${this._t("editor.colors.heating")}</span>
|
|
</label>
|
|
<label class="color-field">
|
|
<input
|
|
type="color"
|
|
.value=${this._config.color?.hvac?.cooling ?? "#4dabf7"}
|
|
@input=${(o) => this._handleTextChange("color.hvac.cooling", o)}
|
|
/>
|
|
<span>${this._t("editor.colors.cooling")}</span>
|
|
</label>
|
|
<label class="color-field">
|
|
<input
|
|
type="color"
|
|
.value=${this._config.color?.hvac?.off ?? "#868e96"}
|
|
@input=${(o) => this._handleTextChange("color.hvac.off", o)}
|
|
/>
|
|
<span>${this._t("editor.colors.off")}</span>
|
|
</label>
|
|
</div>
|
|
${this._renderDocsLink("color")}
|
|
</div>
|
|
</ha-expansion-panel>
|
|
|
|
<!-- Size section -->
|
|
<ha-expansion-panel outlined data-testid="size-section">
|
|
<span slot="header">${this._t("editor.size.title")}</span>
|
|
<div class="content">
|
|
<label class="checkbox-field">
|
|
<input
|
|
type="checkbox"
|
|
.checked=${this._config.size?.equal ?? !1}
|
|
@change=${(o) => this._handleBoolChange("size.equal", o)}
|
|
/>
|
|
<span>${this._t("editor.size.equal")}</span>
|
|
</label>
|
|
<label class="checkbox-field">
|
|
<input
|
|
type="checkbox"
|
|
.checked=${this._config.size?.inverse ?? !1}
|
|
.disabled=${this._config.size?.equal ?? !1}
|
|
@change=${(o) => this._handleBoolChange("size.inverse", o)}
|
|
/>
|
|
<span>${this._t("editor.size.inverse")}</span>
|
|
</label>
|
|
<div class="field-row">
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.size.min")}
|
|
.value=${this._config.size?.min ?? ""}
|
|
.disabled=${this._config.size?.equal ?? !1}
|
|
@input=${(o) => this._handleNumberChange("size.min", o)}
|
|
placeholder=${this._t("editor.colors.auto")}
|
|
></ha-textfield>
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.size.max")}
|
|
.value=${this._config.size?.max ?? ""}
|
|
.disabled=${this._config.size?.equal ?? !1}
|
|
@input=${(o) => this._handleNumberChange("size.max", o)}
|
|
placeholder=${this._t("editor.colors.auto")}
|
|
></ha-textfield>
|
|
</div>
|
|
${this._renderDocsLink("size")}
|
|
</div>
|
|
</ha-expansion-panel>
|
|
|
|
<!-- Data section (order, filter, limit) -->
|
|
<ha-expansion-panel outlined data-testid="data-section">
|
|
<span slot="header">${this._t("editor.data.title")}</span>
|
|
<div class="content">
|
|
<ha-select
|
|
label=${this._t("editor.data.sort_by")}
|
|
.value=${this._config.sort_by ?? "value"}
|
|
@selected=${(o) => this._handleTextChange("sort_by", o)}
|
|
@closed=${(o) => o.stopPropagation()}
|
|
>
|
|
<ha-list-item value="value">${this._t("editor.data.sort_by_value")}</ha-list-item>
|
|
<ha-list-item value="entity_id"
|
|
>${this._t("editor.data.sort_by_entity_id")}</ha-list-item
|
|
>
|
|
<ha-list-item value="label">${this._t("editor.data.sort_by_label")}</ha-list-item>
|
|
<ha-list-item value="default">${this._t("editor.data.sort_by_default")}</ha-list-item>
|
|
</ha-select>
|
|
<ha-select
|
|
label=${this._t("editor.data.order")}
|
|
.value=${this._config.order ?? "desc"}
|
|
@selected=${(o) => this._handleTextChange("order", o)}
|
|
@closed=${(o) => o.stopPropagation()}
|
|
>
|
|
<ha-list-item value="desc">${this._t("editor.data.desc")}</ha-list-item>
|
|
<ha-list-item value="asc">${this._t("editor.data.asc")}</ha-list-item>
|
|
</ha-select>
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.data.limit")}
|
|
.value=${this._config.limit ?? ""}
|
|
@input=${(o) => this._handleNumberChange("limit", o)}
|
|
placeholder=${this._t("editor.data.all")}
|
|
></ha-textfield>
|
|
<div class="field-row">
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.data.above")}
|
|
.value=${this._config.filter?.above ?? ""}
|
|
@input=${(o) => this._handleNumberChange("filter.above", o)}
|
|
></ha-textfield>
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.data.below")}
|
|
.value=${this._config.filter?.below ?? ""}
|
|
@input=${(o) => this._handleNumberChange("filter.below", o)}
|
|
></ha-textfield>
|
|
</div>
|
|
<label class="checkbox-field">
|
|
<input
|
|
type="checkbox"
|
|
.checked=${this._config.filter?.unavailable ?? !1}
|
|
@change=${(o) => this._handleBoolChange("filter.unavailable", o)}
|
|
/>
|
|
<span>${this._t("editor.data.unavailable")}</span>
|
|
</label>
|
|
${this._renderDocsLink("order--filter")}
|
|
</div>
|
|
</ha-expansion-panel>
|
|
|
|
<!-- Layout section -->
|
|
<ha-expansion-panel outlined data-testid="layout-section">
|
|
<span slot="header">${this._t("editor.layout.title")}</span>
|
|
<div class="content">
|
|
<div class="field-row">
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.layout.height")}
|
|
.value=${this._config.height ?? ""}
|
|
@input=${(o) => this._handleNumberChange("height", o)}
|
|
placeholder=${this._t("editor.colors.auto")}
|
|
></ha-textfield>
|
|
<ha-textfield
|
|
type="number"
|
|
label=${this._t("editor.layout.gap")}
|
|
.value=${this._config.gap ?? ""}
|
|
@input=${(o) => this._handleNumberChange("gap", o)}
|
|
placeholder="6"
|
|
></ha-textfield>
|
|
</div>
|
|
${this._renderDocsLink("layout")}
|
|
</div>
|
|
</ha-expansion-panel>
|
|
|
|
<!-- Actions section -->
|
|
<ha-expansion-panel outlined data-testid="actions-section">
|
|
<span slot="header">${this._t("editor.actions.title")}</span>
|
|
<div class="content">
|
|
<ha-form
|
|
.hass=${this.hass}
|
|
.data=${{
|
|
tap_action: this._config.tap_action,
|
|
hold_action: this._config.hold_action
|
|
}}
|
|
.schema=${this._actionsSchema}
|
|
.computeLabel=${(o) => this._t(`editor.actions.${o.name}`)}
|
|
@value-changed=${this._handleActionsChanged}
|
|
data-testid="actions-form"
|
|
></ha-form>
|
|
${this._renderDocsLink("tap--hold-actions")}
|
|
</div>
|
|
</ha-expansion-panel>
|
|
|
|
<!-- Footer banner -->
|
|
<div class="footer-banner">
|
|
<a href=${ne} target="_blank" rel="noopener">
|
|
<ha-icon icon="mdi:book-open-variant"></ha-icon>
|
|
${this._t("editor.footer.docs")}
|
|
</a>
|
|
<span class="separator">|</span>
|
|
<a href="${ne}/issues" target="_blank" rel="noopener">
|
|
<ha-icon icon="mdi:bug-outline"></ha-icon>
|
|
${this._t("editor.footer.issues")}
|
|
</a>
|
|
<span class="separator">|</span>
|
|
<a href=${ne} target="_blank" rel="noopener">
|
|
<ha-icon icon="mdi:star-outline"></ha-icon>
|
|
${this._t("editor.footer.star")}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
};
|
|
H.styles = Wi;
|
|
ue([
|
|
ce({ attribute: !1 })
|
|
], H.prototype, "hass", 2);
|
|
ue([
|
|
ce({ attribute: !1 })
|
|
], H.prototype, "lovelace", 2);
|
|
ue([
|
|
ve()
|
|
], H.prototype, "_config", 2);
|
|
H = ue([
|
|
Je("treemap-card-editor")
|
|
], H);
|
|
const rn = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
__proto__: null,
|
|
get TreemapCardEditor() {
|
|
return H;
|
|
}
|
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
export {
|
|
T as TreemapCard
|
|
};
|