Skip to content

Commit

Permalink
Rerender speed and stability improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasloven committed Mar 1, 2021
1 parent 4da479e commit 27d1689
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 62 deletions.
4 changes: 2 additions & 2 deletions card-mod.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "card-mod",
"private": true,
"version": "3.0.5",
"version": "3.0.6",
"description": "",
"scripts": {
"build": "rollup -c",
Expand Down
82 changes: 32 additions & 50 deletions src/card-mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import pjson from "../package.json";
import { selectTree } from "card-tools/src/helpers";
import {
applyToElement,
compare_deep,
get_theme,
merge_deep,
parentElement,
Expand All @@ -20,7 +21,6 @@ export class CardMod extends LitElement {
_renderer: (_: string) => void;
_styleChildren: Set<CardMod> = new Set();
_input_styles: Styles;
_refreshCooldown = { running: false, repeat: false };

_observer: MutationObserver = new MutationObserver((mutations) => {
for (const m of mutations) {
Expand All @@ -38,7 +38,7 @@ export class CardMod extends LitElement {
});
}

this.refresh(true);
this.refresh();
});

static get applyToElement() {
Expand All @@ -49,39 +49,32 @@ export class CardMod extends LitElement {
super();
document
.querySelector("home-assistant")
.addEventListener("settheme", () => this.refresh(true));
.addEventListener("settheme", () => this.refresh());
}

connectedCallback() {
super.connectedCallback();
this.refresh();
this._connect();
this.setAttribute("slot", "none");
}

disconnectedCallback() {
super.disconnectedCallback();
this._disconnect(false);
this._disconnect();
}

set styles(stl: Styles) {
if (compare_deep(stl, this._input_styles)) return;
this._input_styles = stl;
this.refresh(true);
this._connect();
}

refresh(forced = false) {
if (this._refreshCooldown.running) {
this._refreshCooldown.repeat = true;
return;
}
window.setTimeout(() => {
this._refreshCooldown.running = false;
if (this._refreshCooldown.repeat) this.refresh();
}, 1);
this._refreshCooldown.repeat = false;
this._disconnect(forced).then(() => this._connect(this._input_styles));
refresh() {
this._connect();
}

private async _connect(stl: Styles) {
private async _connect() {
const stl = this._input_styles;
// Always work with yaml styles
let styles = JSON.parse(JSON.stringify(stl || {}));
if (typeof styles === "string") styles = { ".": styles };
Expand All @@ -90,26 +83,36 @@ export class CardMod extends LitElement {
const theme_styles = await get_theme(this);
merge_deep(styles, theme_styles);

const styleChildren: Set<CardMod> = new Set();
const parent = this.parentElement || this.parentNode;
if (!styles["."]) this._styles = "";
for (const [key, value] of Object.entries(styles as object)) {
if (key === ".") {
this._styles = value;
} else {
for (const el of await selectTree(parent, key, true)) {
this._styleChildren.add(
await applyToElement(
el,
`${this.type}-child`,
value,
this.variables,
null,
false
)
);
if (el)
styleChildren.add(
await applyToElement(
el,
`${this.type}-child`,
value,
this.variables,
null,
false
)
);
}
}
}

for (const oldCh of this._styleChildren) {
if (!styleChildren.has(oldCh)) {
if (oldCh) oldCh.styles = "";
}
}
this._styleChildren = styleChildren;

if (this._styles && hasTemplate(this._styles)) {
this._renderer = this._renderer || this._style_rendered.bind(this);
bind_template(this._renderer, this._styles as string, this.variables);
Expand All @@ -118,32 +121,11 @@ export class CardMod extends LitElement {
}

this._observer.observe(parentElement(this), { childList: true });

if (this.type === "card") {
const p = parentElement(parentElement(this)) as any;
if (p) this._observer.observe(p, { childList: true });
if (p && p.updated && !p._cm_update_patched) {
const _updated = p.updated;
const _this = this;
p.updated = function (param) {
_updated.bind(this)(param);
this.updateComplete.then(() => _this.refresh());
};
p._cm_update_patched = true;
}
}
}

private async _disconnect(forced = true) {
private async _disconnect() {
this._observer.disconnect();
await unbind_template(this._renderer);
if (forced) {
this._rendered_styles = "";
for (const c of this._styleChildren) {
if (c) c.styles = "";
this._styleChildren.delete(c);
}
}
}

private _style_rendered(result: string) {
Expand Down
18 changes: 18 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ export function merge_deep(target: any, source: any) {
return target;
}

export function compare_deep(a: any, b: any) {
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (!(a instanceof Object && b instanceof Object)) return false;
for (const x in a) {
if (!a.hasOwnProperty(x)) continue;
if (!b.hasOwnProperty(x)) return false;
if (a[x] === b[x]) continue;
if (typeof a[x] !== "object") return false;
if (!compare_deep(a[x], b[x])) return false;
}
for (const x in b) {
if (!b.hasOwnProperty(x)) continue;
if (!a.hasOwnProperty(x)) return false;
}
return true;
}

export function findConfig(node: LovelaceCard) {
if (node.config) return node.config;
if (node._config) return node._config;
Expand Down
32 changes: 25 additions & 7 deletions src/patch/ha-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,35 @@ customElements.whenDefined("ha-card").then(() => {
null,
false
).then((cardMod) => {
if (this.parentNode?.host?.setConfig) {
const pn = this.parentNode?.host;
if (!pn) return;

if (pn.setConfig && !pn.setConfig.cm_patched) {
// Patch the setConfig function to get live updates in GUI editor
const _setConfig = this.parentNode.host.setConfig;
this.parentNode.host.setConfig = function (config: any) {
const _setConfig = pn.setConfig;
pn.setConfig = function (config: any) {
_setConfig.bind(this)(config);
if (config.card_mod) {
cardMod.variables = { config };
cardMod.styles = config.card_mod;
}
cardMod.variables = { config };
cardMod.styles = config.card_mod || {};
};
pn.setConfig.cm_patched = true;
}

if (pn.update && !pn.update.cm_patched) {
const _update = pn.update;
pn.update = function (changedProperties: any) {
_update.bind(this)(changedProperties);
this.updateComplete.then(() => {
cardMod.refresh();
});
};
pn.update.cm_patched = true;
}

// Try to catch even very slowly loading cards
window.setTimeout(() => cardMod.refresh(), 100);
window.setTimeout(() => cardMod.refresh(), 500);
window.setTimeout(() => cardMod.refresh(), 1000);
});
};

Expand Down
2 changes: 2 additions & 0 deletions src/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export async function bind_template(
const cacheKey = JSON.stringify([template, variables]);
let cache = cachedTemplates[cacheKey];
if (!cache) {
unbind_template(callback);
callback("");

variables = {
Expand All @@ -66,6 +67,7 @@ export async function bind_template(
),
};
} else {
if (!cache.callbacks.has(callback)) unbind_template(callback);
callback(cache.value);
cache.callbacks.add(callback);
}
Expand Down
2 changes: 1 addition & 1 deletion test/.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
HASS_USERNAME=dev
HASS_PASSWORD=dev
LOVELACE_LOCAL_FILES=card-mod.js
LOVELACE_PLUGINS=thomasloven/lovelace-auto-entities custom-cards/button-card
LOVELACE_PLUGINS=thomasloven/lovelace-auto-entities custom-cards/button-card nervetattoo/simple-thermostat

0 comments on commit 27d1689

Please sign in to comment.