UNPKG

@dark-engine/platform-server

Version:
288 lines (287 loc) 10.4 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); exports.toggle = exports.setTrackUpdate = exports.finishCommit = exports.commit = exports.createNativeElement = void 0; const core_1 = require('@dark-engine/core'); const utils_1 = require('../utils'); const events_1 = require('../events'); const constants_1 = require('../constants'); let moves = []; let patches = []; let trackUpdate = null; function createNativeElement(vNode) { switch (vNode.type) { case core_1.NodeType.TAG: const name = vNode.name; const tag = (0, utils_1.detectIsSvgElement)(name) ? document.createElementNS('http://www.w3.org/2000/svg', name) : document.createElement(name); return tag; case core_1.NodeType.TEXT: return document.createTextNode(vNode.value); case core_1.NodeType.COMMENT: return document.createComment(vNode.value); } } exports.createNativeElement = createNativeElement; function setObjectStyle(element, style) { for (const key in style) { m.setStyleProp.call(element.style, key, String(style[key])); } } function addAttributes(element, vNode, isHydration) { const tagElement = element; for (let attrName in vNode.attrs) { const attrValue = vNode.attrs[attrName]; const attribute = performAttribute(tagElement, attrName, attrValue); if (attribute === null) { continue; } else { attrName = attribute; } if ((0, events_1.detectIsEvent)(attrName)) { (0, events_1.delegateEvent)(tagElement, (0, events_1.getEventName)(attrName), attrValue); } else if (!isHydration && !(0, core_1.detectIsUndefined)(attrValue) && !core_1.ATTR_BLACK_LIST[attrName]) { !patchAttributes(tagElement, vNode.name, attrName, attrValue) && m.setAttribute.call(tagElement, attrName, attrValue); } } } function updateAttributes(element, prevVNode, nextVNode) { const attrNames = getAttributeNames(prevVNode, nextVNode); const tagElement = element; for (let attrName of attrNames) { const prevAttrValue = prevVNode.attrs[attrName]; const nextAttrValue = nextVNode.attrs[attrName]; const attribute = performAttribute(tagElement, attrName, nextAttrValue, prevAttrValue); if (attribute === null) { continue; } else { attrName = attribute; } if (!(0, core_1.detectIsUndefined)(nextAttrValue)) { if ((0, events_1.detectIsEvent)(attrName)) { prevAttrValue !== nextAttrValue && (0, events_1.delegateEvent)(tagElement, (0, events_1.getEventName)(attrName), nextAttrValue); } else if (!core_1.ATTR_BLACK_LIST[attrName] && prevAttrValue !== nextAttrValue) { !patchAttributes(tagElement, nextVNode.name, attrName, nextAttrValue) && m.setAttribute.call(tagElement, attrName, nextAttrValue); } } else { m.removeAttribute.call(tagElement, attrName); } } } function performAttribute(tagElement, attrName, nextAttrValue, prevAttrValue) { if (attrName[0] === constants_1.EXCLUDE_ATTR_MARK) return null; if (attrName === constants_1.DANGER_HTML_ATTR) { (0, utils_1.setInnerHTML)(tagElement, String(nextAttrValue)); return null; } if (attrName === core_1.REF_ATTR) { (0, core_1.applyRef)(nextAttrValue, tagElement); return null; } if ( (attrName === constants_1.CLASS_ATTR || attrName === constants_1.CLASS_NAME_ATTR) && nextAttrValue !== prevAttrValue ) { toggleAttribute(tagElement, constants_1.CLASS_ATTR, nextAttrValue); return null; } if ( attrName === constants_1.STYLE_ATTR && nextAttrValue && nextAttrValue !== prevAttrValue && (0, core_1.detectIsObject)(nextAttrValue) ) { setObjectStyle(tagElement, nextAttrValue); return null; } if (attrName === constants_1.PREVENT) { tagElement[constants_1.PREVENT] = true; return null; } if (attrName === constants_1.AS_ATTR) { attrName = attrName.slice(1, constants_1.AS_ATTR.length); } return attrName; } function toggleAttribute(element, name, value) { value ? m.setAttribute.call(element, name, value) : m.removeAttribute.call(element, name); } function getAttributeNames(prevVNode, nextVNode) { const attrNames = new Set(); const prevAttrs = Object.keys(prevVNode.attrs); const nextAttrs = Object.keys(nextVNode.attrs); const size = Math.max(prevAttrs.length, nextAttrs.length); for (let i = 0; i < size; i++) { attrNames.add(prevAttrs[i] || nextAttrs[i]); } return attrNames; } const ATTR_TRANSFORM_MAP = { readonly: 'readOnly', }; function patchAttributes(element, tagName, attrName, attrValue) { const fn = specialCasesMap[tagName]; const $attrName = ATTR_TRANSFORM_MAP[attrName] || attrName; let stop = fn ? fn(element, attrName, attrValue) : false; if (canSetProperty(element, $attrName)) { element[$attrName] = attrValue; } if (!stop && (0, core_1.detectIsBoolean)(attrValue)) { stop = !$attrName.includes(constants_1.DASH_MARK); } return stop; } function canSetProperty(element, key) { const prototype = Object.getPrototypeOf(element); const descriptor = Object.getOwnPropertyDescriptor(prototype, key); return Boolean(descriptor?.set); } const specialCasesMap = { [constants_1.INPUT_TAG]: (element, attrName, attrValue) => { if (attrName === constants_1.VALUE_ATTR) { patches.push(() => { (0, core_1.detectIsBoolean)(attrValue) ? (element.checked = attrValue) : (element.value = String(attrValue)); }); } return false; }, [constants_1.TEXTAREA_TAG]: (element, attrName, attrValue) => { if (attrName === constants_1.VALUE_ATTR) { element.innerText = String(attrValue); return true; } return false; }, }; function commitCreation(fiber) { const parent = (0, core_1.getFiberWithElement)(fiber.parent); const parentElement = parent.el; const childNodes = parentElement.childNodes; const isHydration = (0, core_1.detectIsHydration)(); if (isHydration) { let nativeElement = childNodes[fiber.eidx]; if (nativeElement instanceof DocumentType) { nativeElement = nativeElement.nextSibling; } if ( (0, core_1.detectIsTextVirtualNode)(fiber.inst) && nativeElement instanceof Text && fiber.inst.value.length !== nativeElement.length ) { nativeElement.splitText(fiber.inst.value.length); } if (fiber.el.nodeName !== nativeElement.nodeName) { (0, utils_1.illegal)('Inconsistent element for hydration!'); } fiber.el = nativeElement; } else { if ((0, core_1.detectIsTagVirtualNode)(parent.inst) && parent.inst.attrs[constants_1.DANGER_HTML_ATTR]) { (0, utils_1.illegal)(`The element with danger content can't have a children!`); } if (childNodes.length === 0 || fiber.eidx > childNodes.length - 1) { !(0, utils_1.detectIsVoidElement)(parent.inst.name) && m.appendElement.call(parentElement, fiber.el); } else { m.insertElement.call(parentElement, fiber.el, parentElement.childNodes[fiber.eidx]); } } (0, core_1.detectIsTagVirtualNode)(fiber.inst) && addAttributes(fiber.el, fiber.inst, isHydration); fiber.el.parentElement?.[constants_1.PREVENT] && (fiber.el[constants_1.PREVENT] = true); } function commitUpdate(fiber) { const element = fiber.el; const prevInst = fiber.alt.inst; const nextInst = fiber.inst; (0, core_1.detectIsPlainVirtualNode)(nextInst) ? prevInst.value !== nextInst.value && (element.nodeValue = nextInst.value) : updateAttributes(element, prevInst, nextInst); } function commitDeletion(fiber) { const parent = (0, core_1.getFiberWithElement)(fiber.parent); if (fiber.mask & core_1.FLUSH_MASK) { parent.el.children.length > 0 && (0, utils_1.removeContent)(parent.el); } else { (0, core_1.walk)(fiber, onWalkInCommitDeletion(parent.el)); } } const onWalkInCommitDeletion = parentElement => (fiber, skip) => { if (fiber.el) { !fiber.hook?.getIsPortal() && m.removeElement.call(parentElement, fiber.el); return skip(); } }; function move(fiber) { const sourceNodes = (0, core_1.collectElements)(fiber, x => x.el); const sourceNode = sourceNodes[0]; const parentElement = sourceNode.parentElement; const sourceFragment = new DocumentFragment(); const elementIdx = fiber.eidx; const move = () => { for (let i = 1; i < sourceNodes.length; i++) { m.removeElement.call(parentElement, parentElement.childNodes[elementIdx + 1]); } m.replaceElement.call(parentElement, sourceFragment, parentElement.childNodes[elementIdx]); }; let idx = 0; for (const node of sourceNodes) { m.insertElement.call(parentElement, document.createComment(`${elementIdx}:${idx}`), node); m.appendElement.call(sourceFragment, node); idx++; } moves.push(move); } function commit(fiber) { switch (fiber.tag) { case core_1.CREATE_EFFECT_TAG: if (!fiber.el || fiber.hook?.getIsPortal()) return; trackUpdate && trackUpdate(fiber.el); commitCreation(fiber); break; case core_1.UPDATE_EFFECT_TAG: fiber.mask & core_1.MOVE_MASK && (move(fiber), (fiber.mask &= ~core_1.MOVE_MASK)); if (!fiber.el || fiber.hook?.getIsPortal()) return; trackUpdate && trackUpdate(fiber.el); commitUpdate(fiber); break; case core_1.DELETE_EFFECT_TAG: commitDeletion(fiber); break; default: break; } } exports.commit = commit; function finishCommit() { moves.forEach(x => x()); patches.forEach(x => x()); moves = []; patches = []; } exports.finishCommit = finishCommit; function setup() { if (!(0, utils_1.detectIsBrowser)()) return {}; const np = Node.prototype; const ep = Element.prototype; return { appendElement: np.appendChild, insertElement: np.insertBefore, replaceElement: np.replaceChild, removeElement: np.removeChild, hasAttribute: ep.hasAttribute, setAttribute: ep.setAttribute, removeAttribute: ep.removeAttribute, setStyleProp: CSSStyleDeclaration.prototype.setProperty, }; } const m = setup(); const setTrackUpdate = fn => (trackUpdate = fn); exports.setTrackUpdate = setTrackUpdate; const toggle = (element, isVisible) => { isVisible ? m.hasAttribute.call(element, constants_1.STYLE_ATTR) && m.removeAttribute.call(element, constants_1.STYLE_ATTR) : m.setStyleProp.call(element.style, 'display', 'none', 'important'); }; exports.toggle = toggle; //# sourceMappingURL=dom.js.map