

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

function apply_opts<E extends HTMLElement>(e: E, o: Opts<E>) {
    if (o.id) e.id = o.id
    if (o.onclick) e.onclick = ev => o.onclick!(ev)
    if (o.onchange) e.onchange = ev => o.onchange!(ev)
    // TODO can we do this properly?
    if (o.for) (e as unknown as HTMLLabelElement).htmlFor = o.for
    if (o.type && (e instanceof HTMLInputElement || e instanceof HTMLLinkElement)) e.type = o.type
    if (o.placeholder && (e instanceof HTMLInputElement)) e.placeholder = o.placeholder
    if (o.href && (e instanceof HTMLAnchorElement || e instanceof HTMLLinkElement)) e.href = o.href
    if (o.rel && (e instanceof HTMLAnchorElement || e instanceof HTMLLinkElement)) e.rel = o.rel
    if (o.title && (e instanceof HTMLAnchorElement || e instanceof HTMLLinkElement)) e.title = o.title
    if (o.method && (e instanceof HTMLFormElement)) e.method = o.method
    if (typeof o?.class == "string") e.classList.add(o.class)
    if (typeof o?.class == "object") e.classList.add(...o.class)
}

export function e<K extends keyof HTMLElementTagNameMap>(name: K, opts: Opts<HTMLElementTagNameMap[K]>, ...children: (HTMLElement | string)[]): HTMLElementTagNameMap[K] {
    const el = document.createElement(name)
    apply_opts(el, opts)
    for (const c of children) {
        if (typeof c == "string") el.textContent += c
        else el.append(c)
    }
    return el
}

