UNPKG

vasille

Version:

The first Developer eXperience Orientated front-end framework (core library).

166 lines (165 loc) 5.52 kB
import { TextNode as AbstractTextNode, DebugNode as AbstractDebugNode, Tag as AbstractTag, IValue, } from "../../index.js"; import { internalError } from "../../core/errors.js"; import { AttributeBinding } from "./binding/attribute.js"; import { DynamicalClassBinding, StaticClassBinding } from "./binding/class.js"; import { stringifyStyleValue, StyleBinding } from "./binding/style.js"; export class TextNode extends AbstractTextNode { compose() { const text = this.input.text; this.node = this.runner.document.createTextNode((text instanceof IValue ? text.$ : text)?.toString() ?? ""); if (text instanceof IValue) { this.handler = (v) => { this.node.replaceData(0, -1, v?.toString() ?? ""); }; text.on(this.handler); } this.parent.appendNode(this.node); } destroy() { this.node.remove(); super.destroy(); } findFirstChild() { return this.node; } } export class DebugNode extends AbstractDebugNode { compose() { const text = this.input.text; this.node = this.runner.document.createComment(text.$?.toString() ?? ""); this.handler = (v) => { this.node.replaceData(0, -1, v?.toString() ?? ""); }; text.on(this.handler); this.parent.appendNode(this.node); } destroy() { this.node.remove(); super.destroy(); } findFirstChild() { return this.node; } } export class Tag extends AbstractTag { compose() { if (!this.name) { throw internalError("wrong Tag constructor call"); } const node = this.runner.document.createElement(this.name); this.node = node; this.applyOptions(this.input); this.parent.appendNode(node); this.input.callback?.(this.node); this.input.slot?.(this); } destroy() { this.node.remove(); super.destroy(); } applyOptions(options) { if (options.attr) { for (const name in options.attr) { const value = options.attr[name]; if (value instanceof IValue) { this.register(new AttributeBinding(this, name, value)); } else { /* istanbul ignore else */ if (typeof value === "boolean") { /* istanbul ignore else */ if (value) { this.node.setAttribute(name, ""); } } else if (value !== null && value !== undefined) { this.node.setAttribute(name, `${value}`); } } } } if (options.class) { for (const item of options.class) { if (item instanceof IValue) { this.register(new DynamicalClassBinding(this, item)); } else if (typeof item == "string") { this.node.classList.add(item); } else { for (const name in item) { const value = item[name]; if (value instanceof IValue) { this.register(new StaticClassBinding(this, name, value)); } else if (value) { this.node.classList.add(name); } else { this.node.classList.remove(name); } } } } } if (options.style && this.node instanceof HTMLElement) { for (const name in options.style) { const value = options.style[name]; if (value instanceof IValue) { this.register(new StyleBinding(this, name, value)); } else { this.node.style.setProperty(name, stringifyStyleValue(value)); } } } if (options.events) { for (const name in options.events) { this.node.addEventListener(name, options.events[name]); } } if (options.bind) { const node = this.node; for (const k in options.bind) { const value = options.bind[k]; if (!(value instanceof IValue)) { node[k] = value; } else { node[k] = value.$; this.watch((v) => { node[k] = v; }, [value]); } } } } } export class Runner { constructor(debugUi, document) { this.debugUi = debugUi; this.document = document; } insertBefore(node, before) { const parent = before.parentElement; /* istanbul ignore else */ if (parent) { parent.insertBefore(node, before); } } appendChild(node, child) { node.appendChild(child); } textNode(text) { return new TextNode({ text }, this); } debugNode(text) { return new DebugNode({ text }, this); } tag(tagName, input, cb) { if (cb) { input.slot = cb; } return new Tag(input, this, tagName); } }