kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
127 lines (112 loc) • 4.19 kB
JavaScript
// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {console as Console} from 'global/window';
import KeplerGlContext from 'components/context';
const MissingComp = () => <div />;
export const ERROR_MSG = {
wrongRecipeType:
`injectComponents takes an array of factories replacement pairs as input, ` +
`each pair be a array as [originalFactory, replacement].`,
noDep: (fac, parent) =>
`${fac.name} is required as a dependency of ${parent.name}, ` +
`but is not provided to injectComponents. It will not be rendered.`,
notFunc: 'factory and its replacement should be a function'
};
export function injector(map = new Map()) {
const cache = new Map(); // map<factory, factory -> ?>
const get = (fac, parent) => {
const factory = map.get(fac);
// factory is not injected
if (!factory) {
Console.error(ERROR_MSG.noDep(fac, parent));
return MissingComp;
}
// check if custom factory deps is declared
const instances =
cache.get(factory) ||
factory(...(factory.deps ? factory.deps.map(dep => get(dep, factory)) : []));
cache.set(fac, instances);
return instances;
};
// if you have two functions that happen to have the exactly same text
// it will be override: 2018-02-05
return {
provide: (factory, replacement) => {
if (!typeCheckRecipe([factory, replacement])) {
return injector(map);
}
return injector(new Map(map).set(factory, replacement));
},
get
};
}
export function typeCheckRecipe(recipe) {
if (!Array.isArray(recipe) || recipe.length < 2) {
Console.error('Error injecting [factory, replacement]', recipe);
Console.error(ERROR_MSG.wrongRecipeType);
return false;
}
const [factory, replacement] = recipe;
if (typeof factory !== 'function') {
Console.error('Error injecting factory: ', factory);
Console.error(ERROR_MSG.notFunc);
return false;
} else if (typeof replacement !== 'function') {
Console.error('Error injecting replacement for: ', factory);
Console.error(ERROR_MSG.notFunc);
return false;
}
return true;
}
const identity = state => state;
// Helper to add reducer state to custom component
export function withState(lenses = [], mapStateToProps = identity, actions = {}) {
return Component => {
const WrappedComponent = ({state, ...props}) => (
<KeplerGlContext.Consumer>
{context => (
<Component
{...lenses.reduce(
(totalState, lens) => ({
...totalState,
...lens(context.selector(state))
}),
props
)}
/>
)}
</KeplerGlContext.Consumer>
);
return connect(
state => ({...mapStateToProps(state), state}),
dispatch =>
Object.keys(actions).reduce(
(accu, key) => ({
...accu,
[key]: bindActionCreators(actions[key], dispatch)
}),
{}
)
)(WrappedComponent);
};
}