aboutsummaryrefslogtreecommitdiff
path: root/src/element.ts
blob: bee4ddba814234431e8f4ed1ba0ba98c7b75e613 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/*
    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 <metamuffin.org>
*/
import { OVar } from "./observable.ts";

interface Opts<E> {
    class?: string[] | string,
    id?: string,
    src?: string,
    for?: string,
    type?: string,
    href?: string,
    onclick?: (e: E) => void,
    onchange?: (e: E) => void,
}

function apply_opts<E extends HTMLElement>(e: E, o: Opts<E>): (() => unknown) | 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.onclick) e.addEventListener("click", () => o.onclick!(e))
    if (o.onchange) e.addEventListener("change", () => o.onchange!(e))
    if (typeof o?.class == "string") e.classList.add(o.class)
    if (typeof o?.class == "object") e.classList.add(...o.class)
}

type EEl<K extends keyof HTMLElementTagNameMap> = string
    | HTMLElement
    | Opts<HTMLElementTagNameMap[K]>
    | OVar<Opts<HTMLElementTagNameMap[K]>>
    | OVar<string>
    | OVar<HTMLElement>
    | OVar<HTMLUListElement> // is this possible with dudplication?
    | OVar<HTMLOListElement>
    | OVar<HTMLInputElement>
    | OVar<HTMLPreElement>
    | OVar<HTMLCanvasElement>
    | OVar<HTMLVideoElement>
    | OVar<HTMLImageElement>
    | OVar<HTMLDetailsElement>
    | OVar<HTMLSpanElement>
    | OVar<HTMLSelectElement>
    | OVar<HTMLLabelElement>
    | OVar<HTMLButtonElement>
    | OVar<HTMLDivElement>
    | OVar<HTMLParagraphElement>
    | undefined;

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)
    return el
}

function e_apply<K extends keyof HTMLElementTagNameMap, C extends EEl<K>>(el: HTMLElementTagNameMap[K], c: C): () => unknown {
    if (typeof c == "undefined") {
        return () => { }
    }
    if (typeof c == "string") {
        el.append(c)
        return () => {
            el.textContent = ""
        }
    }
    else if (c instanceof HTMLElement) {
        el.append(c)
        return () => el.removeChild(c)
    }
    else if (c instanceof OVar) {
        let undo_last: () => unknown;
        return c.onchangeinit(val => {
            if (undo_last) undo_last()
            undo_last = e_apply(el, val)
        })
    }
    else return apply_opts(el, c) ?? (() => { })
}