Comparing version 0.0.2 to 0.0.3
{ | ||
"name": "realdom", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"author": "Fedot Kriutchenko <fodyadev@gmail.com>", | ||
@@ -8,6 +8,4 @@ "description": "", | ||
"type": "module", | ||
"exports": { | ||
}, | ||
"dependencies": { | ||
} | ||
"exports": {}, | ||
"dependencies": {} | ||
} |
202
src/dom.js
@@ -1,98 +0,142 @@ | ||
import { runWithState } from './state.js'; | ||
import { Builder } from './common.js'; | ||
import { textContent, NodeOperation } from './operators.js'; | ||
import { runWithState, getCurrentHandler } from './state.js'; | ||
import { argsToArray, isReactive, kebab } from './common.js'; | ||
const nodeUpdates = new WeakMap(); | ||
class NodeDeferredUpdate { | ||
constructor(pos, funcs, state) { | ||
this.pos = pos; | ||
this.funcs = funcs; | ||
this.state = state; | ||
} | ||
} | ||
let tree = null; | ||
let treeSymbol = Symbol(); | ||
export class Tree { | ||
list = [] | ||
i = 0 | ||
class LazyElement { | ||
constructor(build) { | ||
this.build = build; | ||
} | ||
create(ns) { | ||
const node = this.build(ns); | ||
const updates = nodeUpdates.get(node); | ||
if (updates) for (const {pos, funcs, state} of updates) { | ||
let elem = node; | ||
for (const i of pos) elem = elem.childNodes[i]; | ||
for (const func of funcs) func(elem, state); | ||
tmpl = null | ||
init() { | ||
let ns; | ||
let l = 0, node; | ||
const nodes = []; | ||
for (const item of this.list) switch (item.constructor) { | ||
case NodeOpen: | ||
const elem = document.createElement(item.tag); | ||
if (node) node.appendChild(elem); else this.tmpl = elem; | ||
node = nodes[l] = elem; | ||
l++; | ||
break; | ||
case NodeOperation: | ||
if (!item.rerender) item.func(node, item.names, item.args); | ||
break; | ||
case NodeText: | ||
const text = document.createTextNode(item.value); | ||
nodes[l - 1].appendChild(text); | ||
break; | ||
case NodeClose: | ||
l--; | ||
node = nodes[l - 1]; | ||
break; | ||
} | ||
return node; | ||
} | ||
} | ||
const unwrapContents = (item) => { | ||
if (Builder.isBuilder(item)) item = Builder.launch(item); | ||
if (Array.isArray(item)) return item.flatMap(unwrapContents); | ||
return item; | ||
} | ||
export const Element = (tag, ...contents) => new LazyElement(ns => { | ||
let node, textNode = false; | ||
if (tag === '') { | ||
textNode = true; | ||
node = document.createTextNode(''); | ||
} else if (tag === '!') { | ||
textNode = true; | ||
node = document.createComment('') | ||
} else { | ||
if (tag == 'svg') ns = 'http://www.w3.org/2000/svg'; | ||
if (ns) node = document.createElementNS(ns, tag); | ||
else node = document.createElement(tag); | ||
hydrate(root) { | ||
let l = 0, node; | ||
const path = []; | ||
const nodes = []; | ||
for (const item of this.list) switch (item.constructor) { | ||
case NodeOpen: | ||
node = nodes[l] = node ? node.childNodes[path[l - 1]] : root; | ||
path[l] = 0; | ||
l++; | ||
break; | ||
case NodeOperation: | ||
if (item.rerender) item.func(node, item.names, item.args); | ||
break; | ||
case NodeText: | ||
if (item.rerender) node.textContent = item.value; | ||
break; | ||
case NodeClose: | ||
l--; | ||
node = nodes[l - 1]; | ||
path[l - 1]++; | ||
break; | ||
} | ||
return root; | ||
} | ||
const selfUpdates = new NodeDeferredUpdate([], [], null); | ||
const treeUpdates = []; | ||
update(treeNode) { | ||
const prevTree = tree; | ||
tree = this; | ||
const applyUpdate = (item) => { | ||
if (item.deferred) selfUpdates.funcs.push(item.func); | ||
else item.func(node); | ||
this.i = 0; | ||
treeNode(); | ||
if (!this.tmpl) { this.init(); console.log(this); } | ||
tree = prevTree; | ||
} | ||
} | ||
let cid = -1; | ||
const appendChild = (child) => { | ||
cid++; | ||
const childUpdates = nodeUpdates.get(child); | ||
if (childUpdates) for (const {pos, funcs, state} of childUpdates) { | ||
treeUpdates.push(new NodeDeferredUpdate([cid, ...pos], funcs, state)); | ||
} | ||
node.appendChild(child); | ||
} | ||
const symbol = Symbol(); | ||
function builder(effect) { | ||
let names = []; | ||
const tasks = []; | ||
for (const item of unwrapContents(contents)) { | ||
if (item instanceof NodeOperation) { | ||
applyUpdate(item); | ||
} else if (item instanceof LazyElement) { | ||
appendChild(item.build(ns)); | ||
const call = (...args) => { | ||
if (args.length === 0) { | ||
for (const t of tasks) effect(t.names, t.args); | ||
} else { | ||
if (textNode) applyUpdate(Builder.launch(textContent(item))[0]); | ||
else appendChild(Element('', textContent(item)).build(ns)); | ||
tasks.push({ names, args }); | ||
names = []; | ||
} | ||
return p; | ||
} | ||
const get = (_, k) => { | ||
if (k === treeSymbol) return true; | ||
names.push(k); | ||
return p; | ||
} | ||
let p; return p = new Proxy(call, { get }); | ||
} | ||
if (selfUpdates.funcs.length) treeUpdates.push(selfUpdates); | ||
if (treeUpdates.length) nodeUpdates.set(node, treeUpdates); | ||
return node; | ||
export const Builder = (effect) => new Proxy( | ||
(...a) => builder(effect)(...a), | ||
{ get: (_, k) => builder(effect)[k] } | ||
); | ||
class NodeOpen { | ||
constructor(tag) { this.tag = tag; } | ||
} | ||
class NodeOperation { | ||
constructor(func, names) { this.func = func; this.names = names; } | ||
update(rerender, args) { this.rerender = rerender; this.args = args; } | ||
} | ||
class NodeText { | ||
constructor(value) { this.value = value; } | ||
update(value) { this.rerender = this.value != value; this.value = value; } | ||
} | ||
class NodeClose {} | ||
export const Operator = (reactive, func) => Builder((names, args) => { | ||
(tree.list[tree.i++] ??= new NodeOperation(func, names)).update( | ||
typeof reactive == 'function' ? reactive(args) : reactive, | ||
args | ||
); | ||
}); | ||
export const Component = (func, elem) => { | ||
const namespaced = {}; | ||
return function componentInstance(...args) { | ||
const state = {}; | ||
runWithState(state, func, null, args, null); | ||
return new LazyElement(ns => { | ||
const node = namespaced[ns] ??= elem.build(ns); | ||
const clone = node.cloneNode(true); | ||
const updates = nodeUpdates.get(node); | ||
if (updates?.length) nodeUpdates.set(clone, updates.map(u => | ||
new NodeDeferredUpdate(u.pos, u.funcs, u.state ?? state) | ||
)); | ||
return clone; | ||
}); | ||
export const Element = (tag, ...content) => { | ||
const launch = () => { | ||
tree.list[tree.i++] ??= new NodeOpen( | ||
tag[0].toLowerCase() + kebab(tag.slice(1)) | ||
); | ||
for (const item of content) { | ||
switch (item[treeSymbol]) { | ||
case true: item(); break; | ||
default: (tree.list[tree.i++] ??= new NodeText(item)).update(item); | ||
} | ||
} | ||
tree.list[tree.i++] ??= new NodeClose(); | ||
} | ||
launch[treeSymbol] = true; | ||
return launch; | ||
} | ||
export const Component = func => { | ||
const compTree = new Tree(); | ||
let i = 0; | ||
return (...args) => { | ||
compTree.update(func(...args)); | ||
return compTree.hydrate(compTree.tmpl.cloneNode(true)); | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4828
131