UNPKG

rahisi

Version:

UI library for prototyping ideas for reactive programming.

118 lines (92 loc) 3.75 kB
import { Attribute, BaseElement, ConditionalRenderElement, FocusA, NativeAttribute, OnHandlerA, Renderable, TemplateElement, TextElement, } from "./index"; import { AllHTMLAttributes } from "./jsx"; import { Either, F1, F2, } from "rahisi-type-utils"; type K = keyof HTMLElementEventMap; interface AttributeValue { [name: string]: Either<string> | Either<boolean> | F1<HTMLElementEventMap[K], any> | undefined; } export class React { public static createElement = ( tagName: string | F2<Attribute[], Renderable[], Renderable>, attributes: AllHTMLAttributes<Element> | null, ...children: any[]): Renderable => { if (typeof tagName === "function") { return tagName(attributes as any, children); } const attribs = React.getAttributes(attributes); const kids = React.getChildren(children); return new BaseElement(tagName, attribs, kids); } public static getAttributes = <T = Element>(attributes: AllHTMLAttributes<T> | null) => { const attribs = new Array<Attribute>(); if (attributes) { for (const k of Object.keys(attributes)) { const key = k.toLowerCase().replace("doubleclick", "dblclick"); const attributeValue = (attributes as any)[k]; if (key.startsWith("on")) { const event = key.substring(2) as K; attribs.push(new OnHandlerA(event, attributeValue as F1<HTMLElementEventMap[K], any>)); continue; } switch (key) { case "classname": attribs.push(new NativeAttribute("class", attributeValue as Either<string>)); break; case "htmlfor": attribs.push(new NativeAttribute("for", attributeValue as Either<string>)); break; case "focus": attribs.push(new FocusA(attributeValue as Either<boolean>)); break; default: attribs.push(new NativeAttribute(key, attributeValue as Either<string>)); break; } } } return attribs; } public static getChildren = (children: any[]) => { const kids = new Array<Renderable>(); for (const child of children) { React.appendChild(kids, child); } return kids; } private static appendChild = (kids: Renderable[], child: any) => { // <>{condition && <a>Display when condition is true</a>}</> // if condition is false, the child is a boolean, but we don't want to display anything if (typeof child === "undefined" || typeof child === "boolean" || child === null) { return; } if (Array.isArray(child)) { for (const value of child) { React.appendChild(kids, value); } } else if (typeof child === "string" || typeof child === "number") { kids.push(new TextElement(child.toString())); } else if (child instanceof BaseElement || child instanceof TextElement || child instanceof ConditionalRenderElement || child instanceof TemplateElement) { kids.push(child); } else if (typeof child === "function") { kids.push(new TextElement(child)); } else { kids.push(new TextElement(String(child))); } } }