class VNode {
constructor(type, props, children) {
this.type = type;
this.props = props || {};
this.children = children || [];
}
}
function createElement(type, props, ...children) {
return new VNode(type, props, children);
}
function diff(oldNode, newNode, parentDOM) {
if (!oldNode) {
const newDOM = createDOM(newNode);
parentDOM.appendChild(newDOM);
return newDOM;
}
if (!newNode) {
parentDOM.removeChild(oldNode.dom);
return null;
}
if (oldNode.type !== newNode.type) {
const newDOM = createDOM(newNode);
parentDOM.replaceChild(newDOM, oldNode.dom);
return newDOM;
}
if (typeof oldNode.type === 'string') {
updateDOMProperties(oldNode.dom, oldNode.props, newNode.props);
}
else {
}
updateChildren(oldNode.children, newNode.children, oldNode.dom);
oldNode.props = newNode.props;
oldNode.children = newNode.children;
return oldNode.dom;
}
function updateChildren(oldChildren, newChildren, parentDOM) {
const keyToIndexMap = {};
oldChildren.forEach((child, index) => {
if (child.props.key) {
keyToIndexMap[child.props.key] = index;
}
});
newChildren.forEach((newChild, newIndex) => {
const key = newChild.props.key;
if (key) {
const oldIndex = keyToIndexMap[key];
if (oldIndex !== undefined) {
const oldChild = oldChildren[oldIndex];
if (oldIndex !== newIndex) {
parentDOM.insertBefore(oldChild.dom, parentDOM.childNodes[newIndex] || null);
oldChildren.splice(oldIndex, 1);
oldChildren.splice(newIndex, 0, oldChild);
}
diff(oldChild, newChild, parentDOM);
} else {
const newDOM = createDOM(newChild);
parentDOM.insertBefore(newDOM, parentDOM.childNodes[newIndex] || null);
}
} else {
const oldChild = oldChildren[newIndex];
if (oldChild) {
diff(oldChild, newChild, parentDOM);
} else {
const newDOM = createDOM(newChild);
parentDOM.appendChild(newDOM);
}
}
});
while (oldChildren.length > newChildren.length) {
const oldChild = oldChildren.pop();
parentDOM.removeChild(oldChild.dom);
}
}
function createDOM(vnode) {
let dom;
if (typeof vnode.type === 'string') {
dom = document.createElement(vnode.type);
for (const [key, value] of Object.entries(vnode.props)) {
if (key === 'key') continue;
if (key === 'children') continue;
dom[key] = value;
}
vnode.children.forEach(child => {
if (typeof child === 'string') {
dom.appendChild(document.createTextNode(child));
} else {
dom.appendChild(createDOM(child));
}
});
} else {
dom = document.createElement('div');
}
vnode.dom = dom;
return dom;
}
function updateDOMProperties(dom, oldProps, newProps) {
for (const [key, value] of Object.entries(oldProps)) {
if (key === 'key') continue;
if (key === 'children') continue;
if (!(key in newProps)) {
dom[key] = null;
}
}
for (const [key, value] of Object.entries(newProps)) {
if (key === 'key') continue;
if (key === 'children') continue;
dom[key] = value;
}
}