UNPKG

apprun

Version:

JavaScript library that has Elm inspired architecture, event pub-sub and components

239 lines 8.5 kB
import createComponent from './createComponent'; const ATTR_PROPS = '_props'; function collect(children) { const ch = []; const push = (c) => { if (c !== null && c !== undefined && c !== '' && c !== false) { ch.push((typeof c === 'function' || typeof c === 'object') ? c : `${c}`); } }; children && children.forEach(c => { if (Array.isArray(c)) { c.forEach(i => push(i)); } else { push(c); } }); return ch; } export function createElement(tag, props, ...children) { const ch = collect(children); if (typeof tag === 'string') return { tag, props, children: ch }; else if (Array.isArray(tag)) return tag; // JSX fragments - babel else if (tag === undefined && children) return ch; // JSX fragments - typescript else if (Object.getPrototypeOf(tag).__isAppRunComponent) return { tag, props, children: ch }; // createComponent(tag, { ...props, children }); else if (typeof tag === 'function') return tag(props, ch); else throw new Error(`Unknown tag in vdom ${tag}`); } ; const keyCache = new WeakMap(); export const updateElement = render; export function render(element, nodes, parent = {}) { // console.log('render', element, node); // tslint:disable-next-line if (nodes == null || nodes === false) return; nodes = createComponent(nodes, parent); const isSvg = (element === null || element === void 0 ? void 0 : element.nodeName) === "SVG"; if (!element) return; if (Array.isArray(nodes)) { updateChildren(element, nodes, isSvg); } else { updateChildren(element, [nodes], isSvg); } } function same(el, node) { // if (!el || !node) return false; const key1 = el.nodeName; const key2 = `${node.tag || ''}`; return key1.toUpperCase() === key2.toUpperCase(); } function update(element, node, isSvg) { if (node['_op'] === 3) return; // console.assert(!!element); isSvg = isSvg || node.tag === "svg"; if (!same(element, node)) { element.parentNode.replaceChild(create(node, isSvg), element); return; } !(node['_op'] & 2) && updateChildren(element, node.children, isSvg); !(node['_op'] & 1) && updateProps(element, node.props, isSvg); } function updateChildren(element, children, isSvg) { var _a; const old_len = ((_a = element.childNodes) === null || _a === void 0 ? void 0 : _a.length) || 0; const new_len = (children === null || children === void 0 ? void 0 : children.length) || 0; const len = Math.min(old_len, new_len); for (let i = 0; i < len; i++) { const child = children[i]; if (child['_op'] === 3) continue; const el = element.childNodes[i]; if (typeof child === 'string') { if (el.textContent !== child) { if (el.nodeType === 3) { el.nodeValue = child; } else { element.replaceChild(createText(child), el); } } } else if (child instanceof HTMLElement || child instanceof SVGElement) { element.insertBefore(child, el); } else { const key = child.props && child.props['key']; if (key) { if (el.key === key) { update(element.childNodes[i], child, isSvg); } else { // console.log(el.key, key); const old = keyCache[key]; if (old) { const temp = old.nextSibling; element.insertBefore(old, el); temp ? element.insertBefore(el, temp) : element.appendChild(el); } update(element.childNodes[i], child, isSvg); } } else { update(element.childNodes[i], child, isSvg); } } } let n = element.childNodes.length; while (n > len) { element.removeChild(element.lastChild); n--; } if (new_len > len) { const d = document.createDocumentFragment(); for (let i = len; i < children.length; i++) { d.appendChild(create(children[i], isSvg)); } element.appendChild(d); } } function createText(node) { if (node.indexOf('_html:') === 0) { // ? const div = document.createElement('div'); div.insertAdjacentHTML('afterbegin', node.substring(6)); return div; } else { return document.createTextNode(node); } } function create(node, isSvg) { // console.assert(node !== null && node !== undefined); if ((node instanceof HTMLElement) || (node instanceof SVGElement)) return node; if (typeof node === "string") return createText(node); if (!node.tag || (typeof node.tag === 'function')) return createText(JSON.stringify(node)); isSvg = isSvg || node.tag === "svg"; const element = isSvg ? document.createElementNS("http://www.w3.org/2000/svg", node.tag) : document.createElement(node.tag); updateProps(element, node.props, isSvg); if (node.children) node.children.forEach(child => element.appendChild(create(child, isSvg))); return element; } function mergeProps(oldProps, newProps) { newProps['class'] = newProps['class'] || newProps['className']; delete newProps['className']; const props = {}; if (oldProps) Object.keys(oldProps).forEach(p => props[p] = null); if (newProps) Object.keys(newProps).forEach(p => props[p] = newProps[p]); return props; } function updateProps(element, props, isSvg) { // console.assert(!!element); const cached = element[ATTR_PROPS] || {}; props = mergeProps(cached, props || {}); element[ATTR_PROPS] = props; for (const name in props) { const value = props[name]; // if (cached[name] === value) continue; // console.log('updateProps', name, value); if (name.startsWith('data-')) { const dname = name.substring(5); const cname = dname.replace(/-(\w)/g, (match) => match[1].toUpperCase()); if (element.dataset[cname] !== value) { if (value || value === "") element.dataset[cname] = value; else delete element.dataset[cname]; } } else if (name === 'style') { if (element.style.cssText) element.style.cssText = ''; if (typeof value === 'string') element.style.cssText = value; else { for (const s in value) { if (element.style[s] !== value[s]) element.style[s] = value[s]; } } } else if (name.startsWith('xlink')) { const xname = name.replace('xlink', '').toLowerCase(); if (value == null || value === false) { element.removeAttributeNS('http://www.w3.org/1999/xlink', xname); } else { element.setAttributeNS('http://www.w3.org/1999/xlink', xname, value); } } else if (name.startsWith('on')) { if (!value || typeof value === 'function') { element[name] = value; } else if (typeof value === 'string') { if (value) element.setAttribute(name, value); else element.removeAttribute(name); } } else if (/^id$|^class$|^list$|^readonly$|^contenteditable$|^role|-/g.test(name) || isSvg) { if (element.getAttribute(name) !== value) { if (value) element.setAttribute(name, value); else element.removeAttribute(name); } } else if (element[name] !== value) { element[name] = value; } if (name === 'key' && value) keyCache[value] = element; } if (props && typeof props['ref'] === 'function') { window.requestAnimationFrame(() => props['ref'](element)); } } export function Fragment(props, ...children) { return collect(children); } //# sourceMappingURL=vdom-my.js.map