UNPKG

terriajs

Version:

Geospatial data visualization platform.

129 lines (109 loc) 4.34 kB
'use strict'; import React from 'react'; import AssociativeArray from 'terriajs-cesium/Source/Core/AssociativeArray'; import arraysAreEqual from '../../Core/arraysAreEqual'; const types = new AssociativeArray(); /** * A store of registered custom component types, eg. <chart>. */ const CustomComponents = { register: function(customComponentType) { types.set(customComponentType.name, customComponentType); }, // isCustomComponent: function(componentName) { // return (types.contains(componentName.toLowerCase())); // }, names: function() { return types.values.map(customComponentType=>customComponentType.name); }, isRegistered: function(name) { return this.names().indexOf(name) >= 0; }, attributes: function() { const nestedArrays = types.values.map(customComponentType=>customComponentType.attributes); // Flatten the array. return nestedArrays.reduce(function(a, b) { return a.concat(b); }, []); }, values: function() { return types.values; }, /** * Traverses a tree of react components and returns all custom components, based on their "isCorresponding" function. * @param {ReactComponent} tree The tree of react components to traverse. * @return {Object[]} An array of the custom components as objects {type: CustomComponentType, reactComponent: ReactComponent}. */ find: findCustomComponentsInTree, /** * Select the relevant updateCounter from the updateCounters object, by matching against the reactComponent type and key props. * Used by potentially self-updating components. * updateCounters is passed as context to CustomComponentType's processChartNode function. * @param {Object} context.updateCounters Used for self-updating components. An object whose keys are timeoutIds, and whose values are * {reactComponent: ReactComponent, counter: Integer}. reactComponent is selected by this.isCorresponding. * @param {ReactComponent} reactComponentType Eg. The constructor of Chart.jsx. * @param {Object} keyProps If the values of these props match those of the updateCounters' reactComponent, we declare a match. * @return {Integer} The updateCounter for this reactComponent, or undefined if none. */ getUpdateCounter: getUpdateCounter }; /** * @private */ function findCustomComponentsInTree(tree) { // Similar to logic in React-shallow-test-utils. if (!tree) { return []; } let found = []; types.values.forEach(customComponentType=>{ if (customComponentType.isCorresponding(tree)) { found = found.concat({type: customComponentType, reactComponent: tree}); } }); if (React.isValidElement(tree)) { if (React.Children.count(tree.props.children) > 0) { React.Children.forEach(tree.props.children, function(child) { found = found.concat(findCustomComponentsInTree(child)); }); } } return found; } /** * @private */ function getUpdateCounter(updateCounters, reactComponentType, keyProps) { /** * @private */ function haveTheKeyProps(theseProps) { for (const key in keyProps) { if (keyProps.hasOwnProperty(key)) { if (Array.isArray(keyProps[key])) { if (!arraysAreEqual(theseProps[key], keyProps[key])) { return false; } } else if (theseProps[key] !== keyProps[key]) { return false; } } } return true; } if (!updateCounters) { return; } for (const key in updateCounters) { if (updateCounters.hasOwnProperty(key)) { const updateObject = updateCounters[key]; const reactComponent = updateObject.reactComponent; if (reactComponent.type === reactComponentType) { if (haveTheKeyProps(reactComponent.props)) { return updateObject.counter; } } } } } module.exports = CustomComponents;