@turbox3d/renderer-core
Version:
Large-scale declarative graphic ui core renderer
363 lines (362 loc) • 11.8 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.VirtualNode = void 0;
exports.batchUpdate = batchUpdate;
exports.g = g;
exports.render = render;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _shared = require("@turbox3d/shared");
var _scene = require("./scene");
var _mesh = require("./mesh");
var _common = require("./common");
var VirtualNode = exports.VirtualNode = /*#__PURE__*/function () {
function VirtualNode() {
(0, _classCallCheck2["default"])(this, VirtualNode);
this.status = _common.NodeStatus.READY;
this.tag = _common.NodeTag.COMPONENT;
this.props = {};
this.committing = false;
}
return (0, _createClass2["default"])(VirtualNode, [{
key: "validate",
value: function validate() {
var childNodes = this.getChildren();
var map = new Map();
for (var i = 0; i < childNodes.length; i++) {
var node = childNodes[i];
if (!map.has(node.instance.constructor)) {
map.set(node.instance.constructor, [node]);
} else {
var nodes = map.get(node.instance.constructor);
var keys = nodes === null || nodes === void 0 ? void 0 : nodes.map(function (n) {
return n.key;
});
// duplicate key
(0, _shared.invariant)(!(keys === null || keys === void 0 ? void 0 : keys.includes(node.key)), node.key === void 0 ? 'You must specific unique key encountered with the same child elements.' : "Encountered two child elements with the same key, ".concat(node.key, ". Keys should be unique."));
nodes === null || nodes === void 0 ? void 0 : nodes.push(node);
}
}
return map;
}
}, {
key: "link",
value: function link(elements) {
var _this = this;
var els = elements.filter(function (e) {
return (0, _shared.isPlainObject)(e);
});
if (!els.length) {
return;
}
this.child = els.reduceRight(function (previous, current) {
var node = VirtualNode.buildNode(current);
node.sibling = previous;
node.parent = _this;
return node;
}, undefined);
this.validate();
return this.child;
}
}, {
key: "create",
value: function create() {
var _this2 = this;
var singleNode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
this.instance.componentWillMount();
var child;
var children = this.instance.render();
if (children && children.length) {
child = this.link(children);
}
if (child) {
child.create();
}
if (VirtualNode.isBatchUpdate) {
VirtualNode.batchQueue.push(function () {
return _this2.commitCreate();
});
} else {
this.commitCreate();
}
if (singleNode) {
return;
}
var sibling = this.sibling;
if (!sibling) {
return;
}
sibling.create();
}
}, {
key: "commitCreate",
value: function commitCreate() {
// create first, then delete
if (this.status & _common.NodeStatus.REMOVE) {
return this;
}
if (this.instance instanceof _mesh.BaseMesh) {
this.instance.commit('create');
}
this.instance.componentDidMount();
return this;
}
}, {
key: "getChildren",
value: function getChildren() {
if (!this.child) {
return [];
}
var nodes = [this.child];
var current = this.child;
while (current.sibling) {
nodes.push(current.sibling);
current = current.sibling;
}
return nodes;
}
}, {
key: "remove",
value: function remove() {
var childNodes = this.getChildren();
if (childNodes.length) {
childNodes.forEach(function (n) {
n.status |= _common.NodeStatus.REMOVE;
n.remove();
});
}
this.commitDelete();
}
}, {
key: "commitDelete",
value: function commitDelete() {
this.instance.componentWillUnmount();
if (this.instance instanceof _mesh.BaseMesh) {
this.instance.commit('delete');
}
}
}, {
key: "patch",
value: function patch() {
var _this3 = this;
// apply remove first, then update or create by order.
var childNodes = this.getChildren();
if (!childNodes.length) {
return;
}
childNodes.reduce(function (prev, current) {
if (current.status & _common.NodeStatus.REMOVE) {
current.remove();
if (prev === undefined) {
_this3.child = current.sibling;
} else {
prev.sibling = current.sibling;
}
current.sibling = undefined;
current.parent = undefined;
return prev;
}
return current;
}, undefined);
var newChildNodes = this.getChildren();
for (var index = 0; index < newChildNodes.length; index++) {
var n = newChildNodes[index];
if (n.status & _common.NodeStatus.UPDATE) {
n.update();
} else if (n.status & _common.NodeStatus.CREATE) {
n.create(true);
}
}
}
}, {
key: "resetStatus",
value: function resetStatus() {
this.status = _common.NodeStatus.READY;
// const childNodes = this.getChildren();
// childNodes.forEach(n => {
// n.status = NodeStatus.READY;
// });
}
}, {
key: "diff",
value: function diff(elements) {
var _this4 = this;
var childNodes = this.getChildren();
var els = elements && elements.filter(function (e) {
return (0, _shared.isPlainObject)(e);
});
if (els && els.length) {
var map = this.validate();
var previous;
els.forEach(function (el) {
var _a;
var tempNodes = map.get(el.type);
var keys = (tempNodes === null || tempNodes === void 0 ? void 0 : tempNodes.map(function (n) {
return n.key;
})) || [];
if (tempNodes && keys.includes(((_a = el.props) === null || _a === void 0 ? void 0 : _a.key) || undefined)) {
// update
var node = tempNodes.find(function (n) {
var _a;
return n.key === (((_a = el.props) === null || _a === void 0 ? void 0 : _a.key) || undefined);
});
if (node) {
var needUpdate = node.instance.shouldComponentUpdate(el.props);
node.status |= needUpdate ? _common.NodeStatus.UPDATE : _common.NodeStatus.FAKE_UPDATE;
if (needUpdate) {
var needUpdateInteractive = node.instance.shouldComponentInteractiveUpdate(el.props);
if (needUpdateInteractive) {
node.status |= _common.NodeStatus.UPDATE_INTERACTIVE;
}
var needUpdateCustomProps = node.instance.shouldComponentCustomPropsUpdate(el.props);
if (needUpdateCustomProps) {
node.status |= _common.NodeStatus.UPDATE_CUSTOM_PROPS;
}
node.props = el.props;
}
(0, _shared.remove)(tempNodes, node);
previous = node;
}
} else {
// create
var _node = VirtualNode.buildNode(el);
_node.status |= _common.NodeStatus.CREATE;
_node.parent = _this4;
if (!previous) {
var tn = _this4.child;
_this4.child = _node;
_node.sibling = tn;
} else {
var tempNode = previous.sibling;
previous.sibling = _node;
_node.sibling = tempNode;
}
}
});
// remove
(0, _toConsumableArray2["default"])(map.values()).flat().forEach(function (n) {
n.status |= _common.NodeStatus.REMOVE;
});
} else {
childNodes.forEach(function (n) {
n.status |= _common.NodeStatus.REMOVE;
});
}
}
}, {
key: "update",
value: function update() {
var _this5 = this;
var isForce = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
// combine batch update status, once the UPDATE status appears, continue to update
if (!isForce && !(this.status & _common.NodeStatus.UPDATE)) {
return;
}
// delete first, then do not to update or create
if (this.status & _common.NodeStatus.REMOVE) {
return;
}
this.instance.componentWillUpdate(this.props);
var prevProps = this.instance.props;
this.instance.props = this.props || {};
var elements = this.instance.render();
this.diff(elements);
this.patch();
if (VirtualNode.isBatchUpdate) {
VirtualNode.batchQueue.push(function () {
return _this5.commitUpdate(prevProps);
});
} else {
this.commitUpdate(prevProps);
this.committing = false;
this.resetStatus();
}
}
}, {
key: "commitUpdate",
value: function commitUpdate(prevProps) {
// update first, then delete
if (this.status & _common.NodeStatus.REMOVE) {
return this;
}
if (this.instance instanceof _mesh.BaseMesh) {
var needUpdateInteractive = this.status & _common.NodeStatus.UPDATE_INTERACTIVE;
var needUpdateCustomProps = this.status & _common.NodeStatus.UPDATE_CUSTOM_PROPS;
this.instance.commit('update', {
interactive: !!needUpdateInteractive,
customProps: !!needUpdateCustomProps
});
}
this.instance.componentDidUpdate(prevProps);
return this;
}
}, {
key: "getParentPath",
value: function getParentPath() {
var path = [];
// eslint-disable-next-line @typescript-eslint/no-this-alias
var t = this;
while (t.parent) {
t = t.parent;
path.unshift(t);
}
return path;
}
}], [{
key: "buildNode",
value: function buildNode(el) {
var _a;
var node = new VirtualNode();
node.props = el.props;
var RenderComponent = el.type;
var rc = new RenderComponent(el.props);
if (rc instanceof _scene.BaseScene) {
node.tag = _common.NodeTag.SCENE;
} else if (rc instanceof _mesh.BaseMesh) {
node.tag = _common.NodeTag.MESH;
}
node.instance = rc;
node.key = ((_a = el.props) === null || _a === void 0 ? void 0 : _a.key) || undefined;
rc._vNode = node;
return node;
}
}]);
}();
VirtualNode.isBatchUpdate = false;
VirtualNode.batchQueue = [];
function render(elements) {
var rootNode = new VirtualNode();
rootNode.key = '@@TURBOX__RootNode';
var child = rootNode.link(elements);
if (child) {
child.create();
}
}
function g(type, props) {
return {
type: type,
props: props
};
}
function batchUpdate(callback, finish) {
VirtualNode.isBatchUpdate = true;
callback();
var nodes = [];
// const waitUpdateQueue = VirtualNode.batchQueue.filter(n => !n.getParentPath().some(parent => VirtualNode.batchQueue.indexOf(parent) > -1));
for (var index = 0; index < VirtualNode.batchQueue.length; index++) {
var f = VirtualNode.batchQueue[index];
var node = f();
nodes.push(node);
}
nodes.forEach(function (n) {
n.committing = false;
n.resetStatus();
});
VirtualNode.isBatchUpdate = false;
VirtualNode.batchQueue = [];
finish && finish();
}