aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-12-10 21:07:30 +0100
committermetamuffin <metamuffin@disroot.org>2023-12-10 21:07:30 +0100
commit56fefbe4495d71b7696b4a4a0980b24c5dc26bf4 (patch)
tree415a1090365f5caeb40d20dc096b524364a4bfc1
parent2d36b0762459b8edfc1529827d0c15447edd2669 (diff)
downloadjshelper-56fefbe4495d71b7696b4a4a0980b24c5dc26bf4.tar
jshelper-56fefbe4495d71b7696b4a4a0980b24c5dc26bf4.tar.bz2
jshelper-56fefbe4495d71b7696b4a4a0980b24c5dc26bf4.tar.zst
improve memory leak a little
-rw-r--r--src/element.ts13
-rw-r--r--src/observable.ts16
2 files changed, 25 insertions, 4 deletions
diff --git a/src/element.ts b/src/element.ts
index c5ff4a5..f7dcb04 100644
--- a/src/element.ts
+++ b/src/element.ts
@@ -14,6 +14,8 @@ interface Opts<E> {
href?: string,
style?: { [key in keyof CSSStyleDeclaration]?: CSSStyleDeclaration[key] }
onclick?: (e: E) => void,
+ onmouseenter?: (e: E) => void,
+ onmouseleave?: (e: E) => void,
onchange?: (e: E) => void,
}
@@ -24,6 +26,8 @@ function apply_opts<E extends HTMLElement>(e: E, o: Opts<E>): (() => void) | voi
if (o.href && e instanceof HTMLAnchorElement) e.href = o.href;
if (o.onclick) e.addEventListener("click", () => o.onclick!(e))
if (o.onchange) e.addEventListener("change", () => o.onchange!(e))
+ if (o.onmouseenter) e.addEventListener("mouseenter", () => o.onmouseenter!(e))
+ if (o.onmouseleave) e.addEventListener("mouseleave", () => o.onmouseleave!(e))
if (typeof o?.class == "string") e.classList.add(o.class)
if (typeof o?.class == "object") e.classList.add(...o.class)
if (o.style) for (const k in o.style) { const v = o.style[k]; if (v !== undefined) e.style[k] = v }
@@ -54,7 +58,9 @@ type EEl<K extends keyof HTMLElementTagNameMap> = string
export function e<K extends keyof HTMLElementTagNameMap>(name: K, ...children: EEl<K>[]): HTMLElementTagNameMap[K] {
const el = document.createElement(name)
- for (const c of children) e_apply(el, c)
+ const undo = children.map(c => e_apply(el, c))
+ //@ts-ignore yeye, new prop
+ el["jsh_undo"] = () => undo.forEach(f => f())
return el
}
@@ -73,13 +79,16 @@ function e_apply<K extends keyof HTMLElementTagNameMap, C extends EEl<K>>(el: HT
el.insertBefore(c, redo?.before ?? null)
return () => {
const p = c.nextSibling as (HTMLElement | void);
+ //@ts-ignore wubbel
+ const child_undo: undefined | (() => void) = c["jsh_undo"]
+ if (child_undo) child_undo()
el.removeChild(c)
return { before: p ?? undefined }
}
}
else if (c instanceof OVar) {
let undo_last: () => RedoParams | void;
- // TODO if nested, this is a memory leak
+ // TODO if nested, this is a memory leak (only partially fixed)
return c.onchangeinit(val => {
let redo_param = undefined;
if (undo_last) redo_param = undo_last()
diff --git a/src/observable.ts b/src/observable.ts
index 15a1a2e..5185c9f 100644
--- a/src/observable.ts
+++ b/src/observable.ts
@@ -5,6 +5,9 @@
*/
export class OVar<T> {
private _value: T
+ private weak = false; // if weak, the source will be unsubscribed from, if all listeners are removed
+ private disabled = false
+ private cancel_source?: () => void
private observers: ((v: T) => unknown)[] = []
constructor(initial: T) {
@@ -16,8 +19,16 @@ export class OVar<T> {
change() { this.observers.forEach(o => o(this._value)) }
onchange(handler: (v: T) => unknown): () => void {
+ if (this.disabled) throw new Error("obervable is disabled");
this.observers.push(handler)
- return () => this.observers = this.observers.filter(o => o != handler)
+ if (this.observers.length > 16) console.warn("likely memory leak here:", this);
+ return () => {
+ this.observers = this.observers.filter(o => o != handler)
+ if (this.observers.length == 0 && this.weak) {
+ this.cancel_source!()
+ this.disabled = true
+ }
+ }
}
onchangeinit(handler: (v: T) => unknown): () => void {
const abort = this.onchange(handler)
@@ -26,7 +37,8 @@ export class OVar<T> {
}
map<U>(fn: (v: T) => U): OVar<U> {
const uv = new OVar(fn(this.value))
- this.onchange(v => uv.value = fn(v))
+ uv.cancel_source = this.onchange(v => uv.value = fn(v))
+ uv.weak = true
return uv;
}
wait_for(val: T) {