import { IScope, Scope } from "./Scope"; import { destroy, isNode, isRendition, isWidget, Rendition } from "./traits"; let _scope = Scope.dummy; const beginRender = async () => { } const endRender = () => { } /** Returns the current scope */ export const getScope = () => _scope; /** Schedules the rendition to be rendered to the DOM Node * @param rendition The rendition to be rendered * @param scope The scope */ export const render = async (rendition: unknown, scope = Scope.dummy) => { await beginRender(); const prev = _scope; _scope = scope; try { const node = getItemDom(rendition, scope); scope.own(() => destroy(node)); return node; } finally { _scope = prev; endRender(); } } /** Renders DOM element for different types of the argument. */ export const getItemDom = (v: unknown, scope: IScope) => { if (typeof v === "string" || typeof v === "number" || v instanceof RegExp || v instanceof Date) { // primitive types converted to the text nodes return document.createTextNode(v.toString()); } else if (isNode(v)) { // nodes are kept as is return v; } else if (isRendition(v)) { // renditions are instantiated return v.getDomNode(scope); } else if (isWidget(v)) { // widgets are converted to it's markup return v.domNode; } else if (typeof v === "boolean" || v === null || v === undefined) { // null | undefined | boolean are removed, converted to comments return document.createComment(`[${typeof v} ${String(v)}]`); } else if (v instanceof Array) { // arrays will be translated to document fragments const fragment = document.createDocumentFragment(); v.map(item => getItemDom(item, scope)) .forEach(node => fragment.appendChild(node)); return fragment; } else { // bug: explicit error otherwise throw new Error("Invalid parameter: " + v); } }