UNPKG

@turbox3d/renderer-core

Version:

Large-scale declarative graphic ui core renderer

353 lines 11.2 kB
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck"; import _createClass from "@babel/runtime/helpers/esm/createClass"; import { invariant, remove, isPlainObject } from '@turbox3d/shared'; import { BaseScene } from './scene'; import { BaseMesh } from './mesh'; import { NodeStatus, NodeTag } from './common'; export var VirtualNode = /*#__PURE__*/function () { function VirtualNode() { _classCallCheck(this, VirtualNode); this.status = NodeStatus.READY; this.tag = NodeTag.COMPONENT; this.props = {}; this.committing = false; } return _createClass(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 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 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 & NodeStatus.REMOVE) { return this; } if (this.instance instanceof 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 |= NodeStatus.REMOVE; n.remove(); }); } this.commitDelete(); } }, { key: "commitDelete", value: function commitDelete() { this.instance.componentWillUnmount(); if (this.instance instanceof 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 & 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 & NodeStatus.UPDATE) { n.update(); } else if (n.status & NodeStatus.CREATE) { n.create(true); } } } }, { key: "resetStatus", value: function resetStatus() { this.status = 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 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 ? NodeStatus.UPDATE : NodeStatus.FAKE_UPDATE; if (needUpdate) { var needUpdateInteractive = node.instance.shouldComponentInteractiveUpdate(el.props); if (needUpdateInteractive) { node.status |= NodeStatus.UPDATE_INTERACTIVE; } var needUpdateCustomProps = node.instance.shouldComponentCustomPropsUpdate(el.props); if (needUpdateCustomProps) { node.status |= NodeStatus.UPDATE_CUSTOM_PROPS; } node.props = el.props; } remove(tempNodes, node); previous = node; } } else { // create var _node = VirtualNode.buildNode(el); _node.status |= 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 _toConsumableArray(map.values()).flat().forEach(function (n) { n.status |= NodeStatus.REMOVE; }); } else { childNodes.forEach(function (n) { n.status |= 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 & NodeStatus.UPDATE)) { return; } // delete first, then do not to update or create if (this.status & 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 & NodeStatus.REMOVE) { return this; } if (this.instance instanceof BaseMesh) { var needUpdateInteractive = this.status & NodeStatus.UPDATE_INTERACTIVE; var needUpdateCustomProps = this.status & 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 BaseScene) { node.tag = NodeTag.SCENE; } else if (rc instanceof BaseMesh) { node.tag = 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 = []; export function render(elements) { var rootNode = new VirtualNode(); rootNode.key = '@@TURBOX__RootNode'; var child = rootNode.link(elements); if (child) { child.create(); } } export function g(type, props) { return { type: type, props: props }; } export 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(); }