UNPKG

vasille

Version:

The same framework which is designed to build bulletproof frontends (core library).

175 lines (174 loc) 5.79 kB
import { TextNode as AbstractTextNode, DebugNode as AbstractDebugNode, Tag as AbstractTag, IValue, safe, } from "../../index.js"; import { internalError } from "../../core/errors.js"; import { AttributeBinding } from "./binding/attribute.js"; import { addClass, DynamicalClassBinding, removeClass, StaticClassBinding } from "./binding/class.js"; import { PropertyBinding } from "./binding/property.js"; import { stringifyStyleValue, StyleBinding } from "./binding/style.js"; export class TextNode extends AbstractTextNode { node; compose() { const text = this.data; this.node = this.runner.document.createTextNode((text instanceof IValue ? text.V : 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 { node; compose() { const text = this.data; this.node = this.runner.document.createComment(text.V?.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.options); this.parent.appendNode(node); this.options.callback?.(this.node); this.options.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.bind(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) { options.class.forEach(item => { if (item instanceof IValue) { this.bind(new DynamicalClassBinding(this, item)); } else if (typeof item == "string") { addClass(this, item); } else { for (const name in item) { const value = item[name]; if (value instanceof IValue) { this.bind(new StaticClassBinding(this, name, value)); } else if (value) { addClass(this, name); } else { removeClass(this, name); } } } }); } if (options.style && this.node instanceof HTMLElement) { for (const name in options.style) { const value = options.style[name]; if (value instanceof IValue) { this.bind(new StyleBinding(this, name, value)); } else { this.node.style.setProperty(name, stringifyStyleValue(value)); } } } if (options.events) { for (const name in options.events) { const event = options.events[name]; if (event instanceof Array) { this.node.addEventListener(name, safe(event[0]), event[1]); } else { this.node.addEventListener(name, safe(event)); } } } 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.V; this.bind(new PropertyBinding(this, k, value)); } } } } } export class Runner { debugUi; document; 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); } }