UNPKG

@eeue56/coed

Version:
1,889 lines (1,684 loc) 47.8 kB
import { Maybe } from "@eeue56/ts-core"; export type Tag = | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "blockquote" | "body" | "br" | "button" | "canvas" | "caption" | "cite" | "code" | "col" | "colgroup" | "data" | "datalist" | "dd" | "del" | "details" | "dfn" | "dialog" | "div" | "dl" | "dt" | "em" | "embed" | "fieldset" | "figure" | "footer" | "form" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "head" | "header" | "hgroup" | "hr" | "html" | "i" | "iframe" | "img" | "input" | "ins" | "kbd" | "keygen" | "label" | "legend" | "li" | "link" | "main" | "map" | "mark" | "menu" | "menuitem" | "meta" | "meter" | "nav" | "noscript" | "object" | "ol" | "optgroup" | "option" | "output" | "p" | "param" | "pre" | "progress" | "q" | "rb" | "rp" | "rt" | "rtc" | "ruby" | "s" | "samp" | "script" | "section" | "select" | "small" | "source" | "span" | "strong" | "style" | "sub" | "summary" | "sup" | "table" | "tbody" | "td" | "template" | "textarea" | "tfoot" | "th" | "thead" | "time" | "title" | "tr" | "track" | "u" | "ul" | "var" | "video" | "wbr"; type None = { kind: "none"; }; type StringAttribute = { kind: "string"; key: string; value: string; }; type StyleAttribute = { kind: "style"; key: string; value: string; }; type NumberAttribute = { kind: "number"; key: string; value: string; }; type BooleanAttribute = { kind: "boolean"; key: string; value: boolean; }; /** Used to represent the different types of attributes possible. */ export type Attribute = | None | StringAttribute | NumberAttribute | StyleAttribute | BooleanAttribute; /** Creates a class attribute - classes are combined by the html creator, so you can use it like: ``` html.div([ ], [ class_("one"), class_("two") ], [ ]) ``` */ export function class_(str: string): Attribute { return { kind: "string", key: "class", value: str, }; } /** Creates a style attribute - styles are combined by the html creator, so you can use it like: ``` html.div([ ], [ style_("color", "red"), style_("background-color", "blue") ], [ ]) ``` */ export function style_(key: string, value: string): Attribute { return { kind: "style", key: key, value: value, }; } /** An empty attribute - filtered by the html creator on creation. This is useful if you have a tenary operator, e.g: ``` html.div([ ], [ somethingTruthy ? none() : class_("something") ], [ ]) ``` */ export function none(): Attribute { return { kind: "none", }; } /** Create an attribute with a given key and value. This is set via `setAttribute` at runtime. */ export function attribute(key: string, value: string): Attribute { if (key === "style") return style_(value.split(":")[0], value.split(":")[1]); return { kind: "string", key: key, value: value, }; } /** Create an attribute with a given key and value. This is set via `setAttribute` at runtime. */ export function booleanAttribute(key: string, value: boolean): Attribute { return { kind: "boolean", key: key, value: value, }; } /** Every event has a `name`, like `click`, and a tagger which produces a message of the right type */ export type Event<Msg> = { name: string; tagger(data: any): Msg; }; /** Creates an event handler for passing to a html node */ export function on<Msg>( name: string, tagger: (data: any) => Msg, stopPropagation: boolean = true, preventDefault: boolean = true ): Event<Msg> { return { name: name, tagger: (event: any) => { if (stopPropagation) { event.stopPropagation(); } if (preventDefault) { event.preventDefault(); } return tagger(event); }, }; } /** Special-cased input handler */ export function onInput<Msg>(tagger: (data: string) => Msg): Event<Msg> { return { name: "input", tagger: (event: any) => { event.stopPropagation(); event.preventDefault(); return tagger(event.target.value); }, }; } type TextNode = { kind: "text"; text: string; }; type RegularNode<Msg> = { kind: "regular"; tag: Tag; events: Event<Msg>[]; attributes: Attribute[]; children: HtmlNode<Msg>[]; _eventListeners: any[]; }; type VoidNode<Msg> = { kind: "void"; tag: Tag; events: Event<Msg>[]; attributes: Attribute[]; _eventListeners: any[]; }; /** A HtmlNode is either a text, like: ``` html.text("hello world") ``` Or html, like: ``` html.div([ ], [ ], [ ]) ``` */ export type HtmlNode<Msg> = TextNode | RegularNode<Msg> | VoidNode<Msg>; /** Creates a text node */ export function text(str: string): TextNode { return { kind: "text", text: str, }; } /** Creates a html node with a given tag name, any events, any attributes and any children. */ export function node<Msg>( tag: Tag, events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): RegularNode<Msg> { return { kind: "regular", tag: tag, events: events, attributes: combineAttributes(attributes), children: children, _eventListeners: [ ], }; } /** Creates a void html node with a given tag name, any events, any attributes. */ export function voidNode<Msg>( tag: Tag, events: Event<Msg>[], attributes: Attribute[] ): VoidNode<Msg> { return { kind: "void", tag: tag, events: events, attributes: combineAttributes(attributes), _eventListeners: [ ], }; } function combineAttributes(attributes: Attribute[]): Attribute[] { const knownStringAttributes: { [id: string]: StringAttribute[] } = {}; const knownStyleAttributes: StyleAttribute[] = [ ]; const otherAttributes: Attribute[] = [ ]; // group attribute values attributes.forEach((attribute: Attribute) => { switch (attribute.kind) { case "string": if (!knownStringAttributes[attribute.key]) { knownStringAttributes[attribute.key] = [ ]; } knownStringAttributes[attribute.key].push(attribute); break; case "style": knownStyleAttributes.push(attribute); break; default: otherAttributes.push(attribute); } }); const combinedAttributes: Attribute[] = otherAttributes.filter( (attribute) => attribute.kind !== "none" ); // actually combine attributes together Object.keys(knownStringAttributes).map((key: string) => { combinedAttributes.push( knownStringAttributes[key].reduce( (acc: StringAttribute, currentValue: StringAttribute) => { if (key === "class") { acc.value += " " + currentValue.value; } return acc; } ) ); }); if (knownStyleAttributes.length > 0) { // actually combine attributes together combinedAttributes.push( knownStyleAttributes.reduce( (acc: StringAttribute, currentValue: StyleAttribute) => { if (typeof acc.value === "undefined") acc.value = ""; acc.value += currentValue.key + ":" + currentValue.value + ";"; return acc; }, attribute("style", "") as StringAttribute ) ); } return combinedAttributes; } function renderAttribute(attribute: Attribute): string { switch (attribute.kind) { case "string": if (attribute.value.indexOf('"') > 0) { return `${attribute.key}='${attribute.value}'`; } return `${attribute.key}="${attribute.value}"`; case "number": return `${attribute.key}=${attribute.value}`; case "style": return ""; case "boolean": return attribute.value ? `${attribute.key}="${attribute.key}"` : ""; case "none": return ""; } } /** Renders a HtmlNode tree as a string. */ export function render<Msg>(node: HtmlNode<Msg>, depth = 0): string { const whitespace = " ".repeat(depth * 4); switch (node.kind) { case "text": return whitespace + node.text; case "void": case "regular": const renderedAttributes = node.attributes .map(renderAttribute) .join(" "); const attributes = (renderedAttributes.length > 0 ? " " : "") + renderedAttributes; switch (node.kind) { case "void": return whitespace + `<${node.tag}${attributes}>`; case "regular": { if (node.children.length > 0) { return ( whitespace + `<${node.tag}${attributes}> ${node.children.map((child) => render(child, depth + 1)).join("\n")} ${whitespace}</${node.tag}>` ); } return ( whitespace + `<${node.tag}${attributes}></${node.tag}>` ); } } } } /** Render a node without whitespace */ export function flatRender<Msg>(node: HtmlNode<Msg>): string { switch (node.kind) { case "text": return node.text; case "void": case "regular": const attributes = (node.attributes.length > 0 ? " " : "") + node.attributes.map(renderAttribute).join(" "); switch (node.kind) { case "void": return `<${node.tag}${attributes}>`; case "regular": { if (node.children.length > 0) { return `<${node.tag}${attributes}>${node.children .map((child) => flatRender(child)) .join("")}</${node.tag}>`; } return `<${node.tag}${attributes}></${node.tag}>`; } } } } /** Hydrates a root from a given program. Program must have root set as the string "hydration" */ export function hydrate<Model, Msg>( program: RunningProgram<Model, Msg>, root: Element ) { program.program.root = root as HTMLElement; const node = program.program.view(program.program.initialModel); if (node.kind === "text") return; if (root.children.length === 0) { console.error( "This root has no children. Did you correctly server-side render content?" ); console.error( `Your html should look like <div id="root">{your content}</div>` ); console.error( "The root node should have exactly one child, which is your generated html." ); } hydrateNode(node, program.send, root.children[0]); } /** Attaches event listeners to nodes */ export function hydrateNode<Msg>( node: HtmlNode<Msg>, listener: (msg: Msg) => void, root: Element ): void { switch (node.kind) { case "text": { return; } case "void": case "regular": { node.events.forEach((event: Event<Msg>) => { const listenerFunction = (data: globalThis.Event) => { listener(event.tagger(data)); }; root.addEventListener(event.name, listenerFunction, { once: true, }); node._eventListeners.push({ event: event, listener: listenerFunction, }); }); } } if (node.kind === "regular") { let i = 0; for (const child of node.children) { if (child.kind === "text") continue; const newRoot = root.children[i]; hydrateNode(child, listener, newRoot); i++; } } } /** Builds a HTMLElement tree from a HtmlNode tree, with event triggers being sent to the runner via the listener This function should not be needed by most usage. */ export function buildTree<Msg>( listener: (msg: Msg) => void, node: HtmlNode<Msg> ): HTMLElement | Text { switch (node.kind) { case "text": return document.createTextNode(node.text); case "void": case "regular": { const element = document.createElement(node.tag); node.attributes.forEach((attribute: Attribute) => { setAttributeOnElement(element, attribute); }); node.events.forEach((event: Event<Msg>) => { const listenerFunction = (data: globalThis.Event) => { listener(event.tagger(data)); }; element.addEventListener(event.name, listenerFunction, { once: true, }); node._eventListeners.push({ event: event, listener: listenerFunction, }); }); if (node.kind === "regular") { const children = node.children.map((child) => buildTree(listener, child) ); children.forEach((child) => { element.appendChild(child); }); } return element; } } } /** Triggers the event by name, passing it the payload provided. This function is useful for testing but not much else */ export function triggerEvent<Msg>( eventName: string, payload: any, node: HtmlNode<Msg> ): Maybe.Maybe<Msg> { payload = { stopPropagation: () => undefined, preventDefault: () => undefined, ...payload, }; switch (node.kind) { case "text": return Maybe.Nothing(); case "void": case "regular": const events = node.events.filter( (event) => event.name === eventName ); if (events.length > 0) { return Maybe.Just(events[0].tagger(payload)); } else { return Maybe.Nothing(); } } } /** Converts a `HtmlNode` of type `A` to a `HtmlNode` of type `B`, including children. */ export function map<A, B>(tagger: (a: A) => B, tree: HtmlNode<A>): HtmlNode<B> { switch (tree.kind) { case "text": return tree as HtmlNode<B>; case "void": return voidNode( tree.tag, tree.events.map((event: Event<A>) => { return on(event.name, (data: any) => tagger(event.tagger(data)) ); }), tree.attributes ); case "regular": return node( tree.tag, tree.events.map((event: Event<A>) => { return on(event.name, (data: any) => tagger(event.tagger(data)) ); }), tree.attributes, tree.children.map((child: HtmlNode<A>) => { return map(tagger, child); }) ); } } function isProperty(tag: string, key: string): boolean { switch (tag) { case "INPUT": return ( key === "checked" || key === "indeterminate" || key === "value" || key === "readonly" || key === "disabled" ); case "OPTION": return key === "selected" || key === "disabled"; case "TEXTAREA": return key === "value" || key === "readonly" || key === "disabled"; case "SELECT": return key === "value" || key === "disabled"; case "BUTTON": case "OPTGROUP": return key === "disabled"; } return false; } function setAttributeOnElement( element: HTMLElement, attribute: Attribute ): boolean { switch (attribute.kind) { case "string": case "number": if (isProperty(element.tagName, attribute.key)) { (element as any)[attribute.key] = attribute.value; return true; } else { element.setAttribute(attribute.key, attribute.value); return true; } case "style": element.removeAttribute("style"); const styles = attribute.value.split(";"); for (var i = 0; i < styles.length; i++) { const styleName: string = styles[i].split(":")[0]; const styleValue = styles[i].split(":")[1]; element.style[styleName as any] = styleValue; } return true; case "boolean": { if (attribute.value) { if (isProperty(element.tagName, attribute.key)) { (element as any)[attribute.key] = attribute.value; return true; } element.setAttribute(attribute.key, attribute.key); } else { if (element.getAttribute(attribute.key) === attribute.key) { element.removeAttribute(attribute.key); } } return true; } case "none": return true; } } function patchFacts<Msg>( previousTree: HtmlNode<Msg>, nextTree: HtmlNode<Msg>, elements: HTMLElement ) { switch (nextTree.kind) { case "void": case "regular": { // remove previous attributes that no longer exist on the next dom version if (previousTree.kind === nextTree.kind) { const nextAttributes = [ ]; for (const attr of nextTree.attributes) { if (attr.kind != "none") { nextAttributes.push(attr.key); } } for (const attribute of previousTree.attributes) { if ( attribute.kind !== "none" && nextAttributes.indexOf( ( attribute as | StringAttribute | NumberAttribute | BooleanAttribute | StyleAttribute ).key ) === -1 ) { elements.removeAttribute(attribute.key); } } } nextTree.attributes.forEach((attribute: Attribute) => { setAttributeOnElement(elements, attribute); }); return; } case "text": return; } } function patchEvents<Msg>( listener: (msg: Msg) => void, previousTree: HtmlNode<Msg>, nextTree: HtmlNode<Msg>, elements: HTMLElement ) { switch (nextTree.kind) { case "void": case "regular": ( previousTree as RegularNode<Msg> | VoidNode<Msg> )._eventListeners.forEach((eventListeners) => { elements.removeEventListener( eventListeners.event.name, eventListeners.listener ); }); (nextTree as RegularNode<Msg> | VoidNode<Msg>).events.forEach( (event: Event<Msg>) => { const listenerFunction = (data: globalThis.Event) => { listener(event.tagger(data)); }; elements.addEventListener(event.name, listenerFunction, { once: true, }); nextTree._eventListeners.push({ event: event, listener: listenerFunction, }); } ); return; case "text": return; } } function patch<Msg>( listener: (msg: Msg) => void, currentTree: HtmlNode<Msg>, nextTree: HtmlNode<Msg>, elements: HTMLElement | Text ): HtmlNode<Msg> { if (currentTree.kind != nextTree.kind) { elements.replaceWith(buildTree(listener, nextTree)); return nextTree; } switch (currentTree.kind) { case "text": nextTree = nextTree as TextNode; elements = elements as Text; if (currentTree.text == nextTree.text) { return currentTree; } else { elements.replaceWith(document.createTextNode(nextTree.text)); return nextTree; } case "void": { currentTree = currentTree as VoidNode<Msg>; nextTree = nextTree as VoidNode<Msg>; if (currentTree.tag != nextTree.tag) { elements.replaceWith(buildTree(listener, nextTree)); return nextTree; } else { patchFacts(currentTree, nextTree, elements as HTMLElement); patchEvents( listener, currentTree, nextTree, elements as HTMLElement ); } return nextTree; } case "regular": currentTree = currentTree as RegularNode<Msg>; nextTree = nextTree as RegularNode<Msg>; const currentTreeId = ( currentTree.attributes.filter( (x) => x.kind === "string" && x.key === "id" )[0] as StringAttribute )?.value; const nextTreeId = ( nextTree.attributes.filter( (x) => x.kind === "string" && x.key === "id" )[0] as StringAttribute )?.value; if ( currentTree.tag !== nextTree.tag || currentTreeId !== nextTreeId ) { elements.replaceWith(buildTree(listener, nextTree)); return nextTree; } else { patchFacts(currentTree, nextTree, elements as HTMLElement); patchEvents( listener, currentTree, nextTree, elements as HTMLElement ); const htmlElements = elements as HTMLElement; for (var i = 0; i < nextTree.children.length; i++) { const currentChild = currentTree.children[i]; const nextChild = nextTree.children[i]; const node = htmlElements.childNodes[i]; if (typeof node === "undefined") { htmlElements.appendChild( buildTree(listener, nextChild) ); continue; } switch (node.nodeType) { case Node.ELEMENT_NODE: const element = node as HTMLElement; patch(listener, currentChild, nextChild, element); break; case Node.TEXT_NODE: const text = (node as unknown) as Text; patch(listener, currentChild, nextChild, text); break; } } for ( var i = htmlElements.childNodes.length - 1; i > nextTree.children.length - 1; i-- ) { const node = htmlElements.childNodes[i]; htmlElements.removeChild(node); } } return nextTree; } } /** Every Coed program follows the model-view-update (MVU) pattern made popular on the frontend by Elm. An initial model is given, which is passed to the view function which then populates the `root` element. Any events triggered within the view will use the `update` function to create a new model. Async updates can be handled via the optional `send` callback within the update function. */ export type Program<Model, Msg> = { initialModel: Model; view(model: Model): HtmlNode<Msg>; update(msg: Msg, model: Model, send?: (msg: Msg) => void): Model; root: HTMLElement | "hydration"; }; /** Every running program can be interacted with via `send`. For example you may want to start a program but send some data to it after loading a network request. */ export type RunningProgram<Model, Msg> = { program: Program<Model, Msg>; send: (msg: Msg) => void; }; /** Takes in a program, sets it up and runs it as a main loop */ export function program<Model, Msg>( program: Program<Model, Msg> ): RunningProgram<Model, Msg> { let model = program.initialModel; let previousView = program.view(program.initialModel); let currentTree: HTMLElement | Text | null = null; const listener = (msg: Msg) => { if (currentTree === null) { currentTree = buildTree(listener, previousView); if (program.root !== "hydration") { while (program.root.firstChild) { program.root.removeChild(program.root.firstChild); } program.root.appendChild(currentTree); } } model = program.update(msg, model, listener); const nextView = program.view(model); patch(listener, previousView, nextView, currentTree); previousView = nextView; }; if (program.root !== "hydration") { currentTree = buildTree(listener, previousView); program.root.appendChild(currentTree); } return { program: program, send: listener, }; } // tags export function a<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("a", events, attributes, children); } export function abbr<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("abbr", events, attributes, children); } export function address<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("address", events, attributes, children); } export function area<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("area", events, attributes); } export function article<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("article", events, attributes, children); } export function aside<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("aside", events, attributes, children); } export function audio<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("audio", events, attributes, children); } export function b<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("b", events, attributes, children); } export function base<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("base", events, attributes); } export function bdi<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("bdi", events, attributes, children); } export function bdo<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("bdo", events, attributes, children); } export function blockquote<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("blockquote", events, attributes, children); } export function body<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("body", events, attributes, children); } export function br<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("br", events, attributes); } export function button<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("button", events, attributes, children); } export function canvas<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("canvas", events, attributes, children); } export function caption<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("caption", events, attributes, children); } export function cite<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("cite", events, attributes, children); } export function code<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("code", events, attributes, children); } export function col<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("col", events, attributes); } export function colgroup<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("colgroup", events, attributes, children); } export function data<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("data", events, attributes, children); } export function datalist<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("datalist", events, attributes, children); } export function dd<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("dd", events, attributes, children); } export function del<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("del", events, attributes, children); } export function details<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("details", events, attributes, children); } export function dfn<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("dfn", events, attributes, children); } export function dialog<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("dialog", events, attributes, children); } export function div<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("div", events, attributes, children); } export function dl<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("dl", events, attributes, children); } export function dt<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("dt", events, attributes, children); } export function em<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("em", events, attributes, children); } export function embed<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("embed", events, attributes); } export function fieldset<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("fieldset", events, attributes, children); } export function figure<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("figure", events, attributes, children); } export function footer<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("footer", events, attributes, children); } export function form<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("form", events, attributes, children); } export function h1<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("h1", events, attributes, children); } export function h2<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("h2", events, attributes, children); } export function h3<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("h3", events, attributes, children); } export function h4<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("h4", events, attributes, children); } export function h5<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("h5", events, attributes, children); } export function h6<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("h6", events, attributes, children); } export function head<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("head", events, attributes, children); } export function header<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("header", events, attributes, children); } export function hgroup<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("hgroup", events, attributes, children); } export function hr<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("hr", events, attributes); } export function html<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("html", events, attributes, children); } export function i<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("i", events, attributes, children); } export function iframe<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("iframe", events, attributes, children); } export function img<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("img", events, attributes); } export function input<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("input", events, attributes); } export function ins<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("ins", events, attributes, children); } export function kbd<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("kbd", events, attributes, children); } export function keygen<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("keygen", events, attributes, children); } export function label<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("label", events, attributes, children); } export function legend<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("legend", events, attributes, children); } export function li<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("li", events, attributes, children); } export function link<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("link", events, attributes); } export function main<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("main", events, attributes, children); } export function map_<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("map", events, attributes, children); } export function mark<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("mark", events, attributes, children); } export function menu<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("menu", events, attributes, children); } export function menuitem<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("menuitem", events, attributes, children); } export function meta<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("meta", events, attributes); } export function meter<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("meter", events, attributes, children); } export function nav<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("nav", events, attributes, children); } export function noscript<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("noscript", events, attributes, children); } export function object<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("object", events, attributes, children); } export function ol<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("ol", events, attributes, children); } export function optgroup<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("optgroup", events, attributes, children); } export function option<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("option", events, attributes, children); } export function output<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("output", events, attributes, children); } export function p<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("p", events, attributes, children); } export function param<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("param", events, attributes); } export function pre<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("pre", events, attributes, children); } export function progress<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("progress", events, attributes, children); } export function q<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("q", events, attributes, children); } export function rb<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("rb", events, attributes, children); } export function rp<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("rp", events, attributes, children); } export function rt<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("rt", events, attributes, children); } export function rtc<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("rtc", events, attributes, children); } export function ruby<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("ruby", events, attributes, children); } export function s<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("s", events, attributes, children); } export function samp<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("samp", events, attributes, children); } export function script<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("script", events, attributes, children); } export function section<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("section", events, attributes, children); } export function select<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("select", events, attributes, children); } export function small<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("small", events, attributes, children); } export function source<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("source", events, attributes); } export function span<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("span", events, attributes, children); } export function strong<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("strong", events, attributes, children); } export function style<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("style", events, attributes, children); } export function sub<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("sub", events, attributes, children); } export function summary<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("summary", events, attributes, children); } export function sup<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("sup", events, attributes, children); } export function table<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("table", events, attributes, children); } export function tbody<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("tbody", events, attributes, children); } export function td<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("td", events, attributes, children); } export function template<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("template", events, attributes, children); } export function textarea<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("textarea", events, attributes, children); } export function tfoot<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("tfoot", events, attributes, children); } export function th<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("th", events, attributes, children); } export function thead<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("thead", events, attributes, children); } export function time<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("time", events, attributes, children); } export function title<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("title", events, attributes, children); } export function tr<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("tr", events, attributes, children); } export function track<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("track", events, attributes); } export function u<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("u", events, attributes, children); } export function ul<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("ul", events, attributes, children); } export function var_<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("var", events, attributes, children); } export function video<Msg>( events: Event<Msg>[], attributes: Attribute[], children: HtmlNode<Msg>[] ): HtmlNode<Msg> { return node("video", events, attributes, children); } export function wbr<Msg>( events: Event<Msg>[], attributes: Attribute[] ): HtmlNode<Msg> { return voidNode("wbr", events, attributes); }