Home Reference Source

src/core/factory/FactoryProvider.jsx

import React, { Component, Children, Fragment } from 'react';
import PropTypes from 'prop-types';
import {
  first,
  each,
  isObject,
  isArray,
  isString,
  values,
  isEmpty,
  isNil,
} from 'lodash';

import factoryConfigShape from './factoryConfigShape';
import NotFoundFactory from './NotFoundFactory';
import SecurityCheck from '../auth/SecurityCheck';

class FactoryProvider extends Component {
  getChildContext() {
    return {
      factories: this.factories,
      getComponent: this.getComponent,
      resolveProps: this.resolveProps,
    };
  }

  constructor(props, context) {
    super(props, context);
    this.factories = props.config;
    this.getComponent = this.getComponent.bind(this);
    this.resolveProps = this.resolveProps.bind(this);
    this.checkSecurityAndRender = this.checkSecurityAndRender.bind(this);
  }

  checkSecurityAndRender(component = null, config, level) {
    const { securityBlackList } = this.props;
    if (isEmpty(config) || securityBlackList.includes(level)) return component;
    return props => {
      return (
        <SecurityCheck
          config={config}
          render={({ permissions }) => {
            if (permissions) {
              return React.createElement(component, props);
            }
            return null;
          }}
        />
      );
    };
  }

  getComponent(src, level, security) {
    if (level && this.factories[level] && this.factories[level][src]) {
      return this.checkSecurityAndRender(
        this.factories[level][src],
        security,
        level
      );
    }
    let findedFactory = [];
    each(this.factories, (group, level) => {
      if (group && group[src]) {
        const comp = this.checkSecurityAndRender(group[src], security, level);
        findedFactory.push(comp);
      }
    });

    return first(findedFactory);
  }

  resolveProps(
    props,
    defaultComponent = NotFoundFactory,
    paramName = 'component'
  ) {
    let obj = {};
    if (isObject(props)) {
      Object.keys(props).forEach(key => {
        if (isObject(props[key])) {
          obj[key] = this.resolveProps(props[key], defaultComponent, paramName);
        } else if (key === 'src') {
          obj[paramName] =
            this.getComponent(props[key], null, props.security) ||
            this.checkSecurityAndRender(defaultComponent, props.security);
        } else {
          obj[key] = props[key];
        }
      });
      return isArray(props) ? values(obj) : obj;
    } else if (isString(props)) {
      return this.getComponent(props) || defaultComponent;
    }
    return props;
  }

  render() {
    return Children.only(this.props.children);
  }
}

FactoryProvider.propTypes = {
  config: factoryConfigShape.isRequired,
  securityBlackList: PropTypes.array,
  children: PropTypes.element.isRequired,
};

FactoryProvider.defaultProps = {
  securityBlackList: [],
};

FactoryProvider.childContextTypes = {
  factories: factoryConfigShape.isRequired,
  getComponent: PropTypes.func,
  resolveProps: PropTypes.func,
};

export default FactoryProvider;