/* This file is part of jshelper (https://codeberg.org/metamuffin/jshelper) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2023 metamuffin */ import { OVar } from "./observable.ts"; interface Opts { class?: string[] | string, id?: string, src?: string, for?: string, type?: string, href?: string, style?: { [key in keyof CSSStyleDeclaration]?: CSSStyleDeclaration[key] }, placeholder?: string, onclick?: (e: E) => void, onmouseenter?: (e: E) => void, onmouseleave?: (e: E) => void, onchange?: (e: E) => void, } function apply_opts(e: E, o: Opts): (() => void) | void { if (o.id) e.id = o.id if (o.for) (e as unknown as HTMLLabelElement).htmlFor = o.for if (o.type && e instanceof HTMLInputElement) e.type = o.type if (o.href && e instanceof HTMLAnchorElement) e.href = o.href; if (o.placeholder && e instanceof HTMLInputElement) e.placeholder = o.placeholder; 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 } } type EEl = string | HTMLElement | Opts | OVar> | OVar | OVar | OVar> | OVar // is this possible with dudplication? | OVar | OVar | OVar | OVar | OVar | OVar | OVar | OVar | OVar | OVar | OVar | OVar | OVar | undefined; export function e(name: K, ...children: EEl[]): HTMLElementTagNameMap[K] { const el = document.createElement(name) const undo = children.map(c => e_apply(el, c)) //@ts-ignore yeye, new prop el["jsh_undo"] = () => undo.forEach(f => f()) return el } interface RedoParams { before?: HTMLElement } function e_apply>(el: HTMLElementTagNameMap[K], c: C, redo?: RedoParams): () => RedoParams | void { if (typeof c == "undefined") { return () => { } } if (typeof c == "string") { el.append(c) return () => { el.textContent = "" } } else if (c instanceof HTMLElement) { 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 (only partially fixed) return c.onchangeinit(val => { let redo_param = undefined; if (undo_last) redo_param = undo_last() undo_last = e_apply(el, val, redo_param ?? undefined) }) } else return apply_opts(el, c) ?? (() => { }) }