@dark-engine/platform-desktop
Version:
Dark renderer to desktop platforms like Windows, Linux, macOS via Nodegui and Qt
177 lines (176 loc) • 5.82 kB
JavaScript
import {
REF_ATTR,
ATTR_BLACK_LIST,
CREATE_EFFECT_TAG,
UPDATE_EFFECT_TAG,
DELETE_EFFECT_TAG,
MOVE_MASK,
detectIsUndefined,
detectIsObject,
NodeType,
detectIsTagVirtualNode,
detectIsPlainVirtualNode,
getFiberWithElement,
collectElements,
walk,
applyRef as $applyRef,
} from '@dark-engine/core';
import { TagNativeElement, TextNativeElement, CommentNativeElement } from '../native-element';
import { detectIsEvent } from '../events';
import { HIDDEN_ATTR } from '../constants';
let moves = [];
let callbacks = [];
function createNativeElement(vNode) {
switch (vNode.type) {
case NodeType.TAG:
return new TagNativeElement(vNode.name);
case NodeType.TEXT:
return new TextNativeElement(vNode.value);
case NodeType.COMMENT:
return new CommentNativeElement(vNode.value);
}
}
function applyRef(ref, element) {
$applyRef(ref, element.getNativeView());
}
function addAttributes(element, vNode) {
const tagElement = element;
for (const attrName in vNode.attrs) {
const attrValue = vNode.attrs[attrName];
if (attrName === REF_ATTR) {
applyRef(attrValue, tagElement);
continue;
}
if (detectIsEvent(attrName)) {
attrValue && detectIsObject(attrValue) && addEvents(tagElement, attrValue);
} else if (!detectIsUndefined(attrValue) && !ATTR_BLACK_LIST[attrName]) {
tagElement.setAttribute(attrName, attrValue);
}
}
}
function updateAttributes(element, prevVNode, nextVNode) {
const attrNames = getAttributeNames(prevVNode, nextVNode);
const tagElement = element;
for (const attrName of attrNames) {
const prevAttrValue = prevVNode.attrs[attrName];
const nextAttrValue = nextVNode.attrs[attrName];
if (attrName === REF_ATTR) {
applyRef(prevAttrValue, tagElement);
continue;
}
if (!detectIsUndefined(nextAttrValue)) {
if (detectIsEvent(attrName)) {
nextAttrValue &&
detectIsObject(nextAttrValue) &&
prevAttrValue !== nextAttrValue &&
addEvents(tagElement, nextAttrValue);
} else if (!ATTR_BLACK_LIST[attrName] && prevAttrValue !== nextAttrValue) {
tagElement.setAttribute(attrName, nextAttrValue);
}
} else {
if (detectIsEvent(attrName)) {
prevAttrValue && detectIsObject(prevAttrValue) && removeEvents(tagElement, prevAttrValue);
} else {
tagElement.removeAttribute(attrName);
}
}
}
}
function addEvents(tagElement, value) {
for (const key in value) {
tagElement.addEventListener(key, value[key]);
}
}
function removeEvents(tagElement, value) {
for (const key in value) {
tagElement.removeEventListener(key);
}
}
function commitCreation(fiber) {
const parent = getFiberWithElement(fiber.parent);
const parentElement = parent.el;
const children = parentElement.children;
if (children.length === 0 || fiber.eidx > children.length - 1) {
appendNativeElement(fiber.el, parentElement);
} else {
insertNativeElement(fiber.el, parentElement.children[fiber.eidx], parentElement);
}
detectIsTagVirtualNode(fiber.inst) && addAttributes(fiber.el, fiber.inst);
}
function commitUpdate(fiber) {
const element = fiber.el;
const prevInst = fiber.alt.inst;
const nextInst = fiber.inst;
detectIsPlainVirtualNode(nextInst)
? prevInst.value !== nextInst.value && (element.value = nextInst.value)
: updateAttributes(element, prevInst, nextInst);
}
function commitDeletion(fiber) {
const parent = getFiberWithElement(fiber.parent);
walk(fiber, onWalkInCommitDeletion(parent.el));
}
const onWalkInCommitDeletion = parentElement => (fiber, skip) => {
if (fiber.el) {
removeNativeElement(fiber.el, parentElement);
return skip();
}
};
function move(fiber) {
const sourceNodes = collectElements(fiber, x => x.el);
const sourceNode = sourceNodes[0];
const parentElement = sourceNode.parentElement;
const elementIdx = fiber.eidx;
const move = () => {
for (let i = 0; i < sourceNodes.length; i++) {
insertNativeElement(sourceNodes[i], parentElement.children[elementIdx + i], parentElement);
removeNativeElement(parentElement.children[elementIdx + i + 1], parentElement);
}
};
for (let i = 0; i < sourceNodes.length; i++) {
const node = sourceNodes[i];
insertNativeElement(new CommentNativeElement(`${elementIdx}:${i}`), node, parentElement);
removeNativeElement(node, parentElement);
}
moves.push(move);
}
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;
}
function commit(fiber) {
switch (fiber.tag) {
case CREATE_EFFECT_TAG:
fiber.el && commitCreation(fiber);
break;
case UPDATE_EFFECT_TAG:
fiber.mask & MOVE_MASK && (move(fiber), (fiber.mask &= ~MOVE_MASK));
fiber.el && commitUpdate(fiber);
break;
case DELETE_EFFECT_TAG:
commitDeletion(fiber);
break;
default:
break;
}
}
function finishCommit() {
callbacks.forEach(x => x());
moves.forEach(x => x());
callbacks = [];
moves = [];
}
function runAtTheEndOfCommit(cb) {
callbacks.push(cb);
}
const appendNativeElement = (element, parent) => parent.appendChild(element);
const insertNativeElement = (element, sibling, parent) => parent.insertBefore(element, sibling);
const removeNativeElement = (element, parent) => parent.removeChild(element);
const toggle = (element, isVisible) => element.setAttribute(HIDDEN_ATTR, isVisible);
export { createNativeElement, commit, finishCommit, runAtTheEndOfCommit, toggle };
//# sourceMappingURL=dom.js.map