@brusalk/react-wow-addon
Version:
React-style UI Framework for World of Warcraft AddOns
121 lines (120 loc) • 4.86 kB
JavaScript
;
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;
}