vasille
Version:
The first Developer eXperience Orientated front-end framework (core library).
166 lines (165 loc) • 5.52 kB
JavaScript
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);
}
}