From ac2ea83b60f0a8d1dbd90aceecc2dae4622fd7c7 Mon Sep 17 00:00:00 2001 From: Muthu Kumar Date: Thu, 24 Jun 2021 01:41:59 +0530 Subject: [PATCH] (feat) simple renderDOM implementation Signed-off-by: Muthu Kumar --- .gitignore | 1 + browser-test/index.html | 14 +++++++++++ src/render.ts | 51 +++++++++++++++++++++++++++++++++++++++-- src/util.ts | 9 ++++++++ test.browser.ts | 11 +++++++++ 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 browser-test/index.html create mode 100644 test.browser.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4bb0cfb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +browser-test/test.browser.js \ No newline at end of file diff --git a/browser-test/index.html b/browser-test/index.html new file mode 100644 index 0000000..0c6fbd0 --- /dev/null +++ b/browser-test/index.html @@ -0,0 +1,14 @@ + + + + + Hyperactive! + + + + + +
+ + + diff --git a/src/render.ts b/src/render.ts index 106cf23..7d65f4c 100644 --- a/src/render.ts +++ b/src/render.ts @@ -1,5 +1,8 @@ -import { Nodeish, HTMLNode } from "./Node.ts"; -import { isFalsy, escapeHTML } from "./util.ts"; +/// +/// + +import { Node, Nodeish, HTMLNode } from "./Node.ts"; +import { Falsy, isFalsy, escapeHTML, guessEnv } from "./util.ts"; export function renderHTML(node: Nodeish) { if (isFalsy(node)) return ""; @@ -21,3 +24,47 @@ export function renderHTML(node: Nodeish) { return stringified; } + +type NodeishtoDOM = N extends Falsy + ? "" + : N extends string + ? string + : N extends HTMLNode + ? { innerHTML: string } + : HTMLElement; + +const toDOM = function toDOM(node: N) { + if (isFalsy(node)) return ""; + if (typeof node === "string") return escapeHTML(node); + if (node instanceof HTMLNode) return { innerHTML: node.htmlString }; + + const el = document.createElement(node.tag); + + for (const attr in node.attrs) { + el.setAttribute(attr, node.attrs[attr]); + } + + for (const child of node.children) { + const childNode = toDOM(child); + if (typeof childNode !== "string" && "innerHTML" in childNode) + el.insertAdjacentHTML("beforeend", childNode.innerHTML); + else el.append(childNode); + } + + return el; +} as (node: N) => NodeishtoDOM; + +export function renderDOM< + HyNode extends Node | string, + RootNode extends HTMLElement, +>(rootNode: RootNode, hyNode: HyNode) { + const env = guessEnv(); + if (env !== "browser") + throw new Error( + `renderDOM is meant to be used in the browser.` + + ` Found: '${env || "unknown"}'`, + ); + + const domNode = toDOM(hyNode); + return rootNode.append(domNode); +} diff --git a/src/util.ts b/src/util.ts index 59386f6..c330f12 100644 --- a/src/util.ts +++ b/src/util.ts @@ -14,3 +14,12 @@ export type Falsy = false | "" | 0 | 0n | undefined | null; export const Falsy = new Set([false, "", 0, 0n, undefined, null]); // deno-lint-ignore no-explicit-any export const isFalsy = (n: any): n is Falsy => Falsy.has(n); + +export const guessEnv = () => { + if (typeof window === "undefined") { + // @ts-ignore process is a node global API + if (typeof process === "undefined") return "node"; + else return undefined; + } else if (typeof window.Deno !== "undefined") return "deno"; + else return "browser"; +}; diff --git a/test.browser.ts b/test.browser.ts new file mode 100644 index 0000000..6b39d92 --- /dev/null +++ b/test.browser.ts @@ -0,0 +1,11 @@ +/// +/// + +import { elements, renderDOM } from "./mod.ts"; + +const { div, p } = elements; + +renderDOM( + document.getElementById("root")!, + div({ class: "container" }, p("Hello world")), +);