UNPKG

@brusalk/react-wow-addon

Version:

React-style UI Framework for World of Warcraft AddOns

121 lines (120 loc) 4.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.reconcile = exports.render = void 0; const element_1 = require("./element"); const wow_utils_1 = require("./wow-utils"); let rootInstance = null; function render(element, container) { const prevInstance = rootInstance; const nextInstance = reconcile(container, prevInstance, element); rootInstance = nextInstance; } exports.render = render; function reconcile(parentFrame, instance, element) { if (!instance) { // Create instance assert(element, 'element should not be null'); return instantiate(element, parentFrame); } else if (!element) { // Remove instance cleanupFrames(instance); return null; } else if (instance.element.type !== element.type) { // Replace instance const newInstance = instantiate(element, parentFrame); cleanupFrames(instance); return newInstance; } else if (typeof element.type === 'string') { // Update host element wow_utils_1.updateFrameProperties(instance.hostFrame, instance.element.props, element.props); instance.childInstances = reconcileChildren(instance, element); instance.element = element; return instance; } else if (instance.publicInstance) { // print('reconcile composite', (element.type as any).name, stringify(element.props)); // Update composite instance instance.publicInstance.props = element.props; const childElement = instance.publicInstance.render(); const oldChildInstance = instance.childInstance; const childInstance = reconcile(parentFrame, oldChildInstance, childElement); if (!childInstance) { throw 'Failed to update composite instance'; } instance.hostFrame = childInstance.hostFrame; instance.childInstance = childInstance; instance.element = element; return instance; } else { throw 'Reconciler catch all error'; } } exports.reconcile = reconcile; function cleanupFrames(instance) { // TODO: composite objects need special cleanup, this should be part of reconcile if (instance.childInstances) { instance.childInstances.forEach(child => child && cleanupFrames(child)); } if (instance.childInstance) { cleanupFrames(instance.childInstance); } wow_utils_1.cleanupFrame(instance.hostFrame); } function reconcileChildren(instance, element) { const hostFrame = instance.hostFrame; const childInstances = instance.childInstances; const nextChildElements = element.props.children || []; const newChildInstances = []; const count = Math.max(childInstances.length, nextChildElements.length); for (let i = 0; i < count; i++) { const childInstance = childInstances[i]; const childElement = nextChildElements[i]; const newChildInstance = reconcile(hostFrame, childInstance, childElement); newChildInstances.push(newChildInstance); } return newChildInstances.filter(instance => instance != null); } function instantiate(element, parentFrame) { const { type, props } = element; if (typeof type === 'string') { if (type === element_1.TEXT_ELEMENT) { throw 'Cannot create inline text, yet'; } // print('instantiate', type, stringify(props)); // Instantiate host element const frame = wow_utils_1.createFrame(type, parentFrame, props); wow_utils_1.updateFrameProperties(frame, {}, props); const childElements = props.children || []; const childInstances = childElements.map(child => instantiate(child, frame)); const instance = { hostFrame: frame, element, childInstances, childInstance: null }; return instance; } else { // print('instantiate', (type as any).name, stringify(props)); // Instantiate component element const instance = {}; const publicInstance = createPublicInstance(element, instance); const childElement = publicInstance.render(); const childInstance = instantiate(childElement, parentFrame); const hostFrame = childInstance.hostFrame; const updateProps = { hostFrame, element, childInstance, publicInstance }; Object.assign(instance, updateProps); return instance; } } function createPublicInstance(element, internalInstance) { const { type: ComponentType, props } = element; if (!ComponentType) { throw 'Tried createPublicInstance() with undefined'; } if (typeof ComponentType === 'string') { throw 'Tried createPublicInstance() with string'; } const publicInstance = new ComponentType(props); publicInstance.__internalInstance = internalInstance; return publicInstance; }