function KeplerGlFactory()

in src/components/kepler-gl.js [105:436]


function KeplerGlFactory(
  BottomWidget,
  GeoCoderPanel,
  MapContainer,
  ModalContainer,
  SidePanel,
  PlotContainer,
  NotificationPanel
) {
  /** @typedef {import('./kepler-gl').KeplerGlProps} KeplerGlProps */
  /** @augments React.Component<KeplerGlProps> */
  class KeplerGL extends Component {
    static defaultProps = {
      mapStyles: [],
      mapStylesReplaceDefault: false,
      mapboxApiUrl: DEFAULT_MAPBOX_API_URL,
      width: 800,
      height: 800,
      appName: KEPLER_GL_NAME,
      version: KEPLER_GL_VERSION,
      sidePanelWidth: DIMENSIONS.sidePanel.width,
      theme: {},
      cloudProviders: [],
      readOnly: false
    };

    componentDidMount() {
      this._validateMapboxToken();
      this._loadMapStyle(this.props.mapStyles);
      this._handleResize(this.props);
    }

    componentDidUpdate(prevProps) {
      if (
        // if dimension props has changed
        this.props.height !== prevProps.height ||
        this.props.width !== prevProps.width ||
        // react-map-gl will dispatch updateViewport after this._handleResize is called
        // here we check if this.props.mapState.height is sync with props.height
        this.props.height !== this.props.mapState.height
      ) {
        this._handleResize(this.props);
      }
    }

    root = createRef();
    static contextType = RootContext;

    /* selectors */
    themeSelector = props => props.theme;
    availableThemeSelector = createSelector(this.themeSelector, theme =>
      typeof theme === 'object'
        ? {
            ...basicTheme,
            ...theme
          }
        : theme === THEME.light
        ? themeLT
        : theme === THEME.base
        ? themeBS
        : theme
    );

    availableProviders = createSelector(
      props => props.cloudProviders,
      providers =>
        Array.isArray(providers) && providers.length
          ? {
              hasStorage: providers.some(p => p.hasPrivateStorage()),
              hasShare: providers.some(p => p.hasSharingUrl())
            }
          : {}
    );

    localeMessagesSelector = createSelector(
      props => props.localeMessages,
      customMessages => (customMessages ? mergeMessages(messages, customMessages) : messages)
    );

    /* private methods */
    _validateMapboxToken() {
      const {mapboxApiAccessToken} = this.props;
      if (!validateToken(mapboxApiAccessToken)) {
        Console.warn(MISSING_MAPBOX_TOKEN);
      }
    }

    _handleResize({width, height}) {
      if (!Number.isFinite(width) || !Number.isFinite(height)) {
        Console.warn('width and height is required');
        return;
      }
      this.props.mapStateActions.updateMap({
        width: width / (1 + Number(this.props.mapState.isSplit)),
        height
      });
    }

    _loadMapStyle = () => {
      const defaultStyles = Object.values(this.props.mapStyle.mapStyles);
      // add id to custom map styles if not given
      const customStyles = (this.props.mapStyles || []).map(ms => ({
        ...ms,
        id: ms.id || generateHashId()
      }));

      const allStyles = [...customStyles, ...defaultStyles].reduce(
        (accu, style) => {
          const hasStyleObject = style.style && typeof style.style === 'object';
          accu[hasStyleObject ? 'toLoad' : 'toRequest'][style.id] = style;

          return accu;
        },
        {toLoad: {}, toRequest: {}}
      );

      this.props.mapStyleActions.loadMapStyles(allStyles.toLoad);
      this.props.mapStyleActions.requestMapStyles(allStyles.toRequest);
    };

    render() {
      const {
        // props
        id,
        appName,
        version,
        appWebsite,
        onSaveMap,
        onViewStateChange,
        onDeckInitialized,
        width,
        height,
        mapboxApiAccessToken,
        mapboxApiUrl,
        getMapboxRef,
        deckGlProps,

        // redux state
        mapStyle,
        mapState,
        uiState,
        visState,
        providerState,

        // actions,
        visStateActions,
        mapStateActions,
        mapStyleActions,
        uiStateActions,
        providerActions,

        // readOnly override
        readOnly
      } = this.props;

      const availableProviders = this.availableProviders(this.props);

      const {
        filters,
        layers,
        splitMaps, // this will store support for split map view is necessary
        layerOrder,
        layerBlending,
        layerClasses,
        interactionConfig,
        datasets,
        layerData,
        hoverInfo,
        clicked,
        mousePos,
        animationConfig,
        mapInfo
      } = visState;

      const notificationPanelFields = {
        removeNotification: uiStateActions.removeNotification,
        notifications: uiState.notifications
      };

      const sideFields = {
        appName,
        version,
        appWebsite,
        datasets,
        filters,
        layers,
        layerOrder,
        layerClasses,
        interactionConfig,
        mapStyle,
        mapInfo,
        layerBlending,
        onSaveMap,
        uiState,
        mapStyleActions,
        visStateActions,
        uiStateActions,
        width: this.props.sidePanelWidth,
        availableProviders,
        mapSaved: providerState.mapSaved
      };

      const mapFields = {
        datasets,
        getMapboxRef,
        mapboxApiAccessToken,
        mapboxApiUrl,
        mapState,
        uiState,
        editor: visState.editor,
        mapStyle,
        mapControls: uiState.mapControls,
        layers,
        layerOrder,
        layerData,
        layerBlending,
        filters,
        interactionConfig,
        hoverInfo,
        clicked,
        mousePos,
        readOnly: uiState.readOnly,
        onDeckInitialized,
        onViewStateChange,
        uiStateActions,
        visStateActions,
        mapStateActions,
        animationConfig,
        deckGlProps
      };

      const isSplit = splitMaps && splitMaps.length > 1;
      const containerW = mapState.width * (Number(isSplit) + 1);

      const mapContainers = !isSplit
        ? [<MapContainer key={0} index={0} {...mapFields} mapLayers={null} />]
        : splitMaps.map((settings, index) => (
            <MapContainer
              key={index}
              index={index}
              {...mapFields}
              mapLayers={splitMaps[index].layers}
            />
          ));

      const isExportingImage = uiState.exportImage.exporting;
      const theme = this.availableThemeSelector(this.props);
      const localeMessages = this.localeMessagesSelector(this.props);

      return (
        <RootContext.Provider value={this.root}>
          <IntlProvider locale={uiState.locale} messages={localeMessages[uiState.locale]}>
            <ThemeProvider theme={theme}>
              <GlobalStyle
                width={width}
                height={height}
                className="kepler-gl"
                id={`kepler-gl__${id}`}
                ref={this.root}
              >
                <NotificationPanel {...notificationPanelFields} />
                {!uiState.readOnly && !readOnly && <SidePanel {...sideFields} />}
                <div className="maps" style={{display: 'flex'}}>
                  {mapContainers}
                </div>
                {isExportingImage && (
                  <PlotContainer
                    width={width}
                    height={height}
                    exportImageSetting={uiState.exportImage}
                    mapFields={mapFields}
                    addNotification={uiStateActions.addNotification}
                    setExportImageSetting={uiStateActions.setExportImageSetting}
                    setExportImageDataUri={uiStateActions.setExportImageDataUri}
                    setExportImageError={uiStateActions.setExportImageError}
                    splitMaps={splitMaps}
                  />
                )}
                {interactionConfig.geocoder.enabled && (
                  <GeoCoderPanel
                    isGeocoderEnabled={interactionConfig.geocoder.enabled}
                    mapboxApiAccessToken={mapboxApiAccessToken}
                    mapState={mapState}
                    updateVisData={visStateActions.updateVisData}
                    removeDataset={visStateActions.removeDataset}
                    updateMap={mapStateActions.updateMap}
                  />
                )}
                <BottomWidget
                  filters={filters}
                  datasets={datasets}
                  uiState={uiState}
                  layers={layers}
                  animationConfig={animationConfig}
                  visStateActions={visStateActions}
                  sidePanelWidth={
                    uiState.readOnly ? 0 : this.props.sidePanelWidth + theme.sidePanel.margin.left
                  }
                  containerW={containerW}
                />
                <ModalContainer
                  mapStyle={mapStyle}
                  visState={visState}
                  mapState={mapState}
                  uiState={uiState}
                  mapboxApiAccessToken={mapboxApiAccessToken}
                  mapboxApiUrl={mapboxApiUrl}
                  visStateActions={visStateActions}
                  uiStateActions={uiStateActions}
                  mapStyleActions={mapStyleActions}
                  providerActions={providerActions}
                  rootNode={this.root.current}
                  containerW={containerW}
                  containerH={mapState.height}
                  providerState={this.props.providerState}
                  // User defined cloud provider props
                  cloudProviders={this.props.cloudProviders}
                  onExportToCloudSuccess={this.props.onExportToCloudSuccess}
                  onLoadCloudMapSuccess={this.props.onLoadCloudMapSuccess}
                  onLoadCloudMapError={this.props.onLoadCloudMapError}
                  onExportToCloudError={this.props.onExportToCloudError}
                />
              </GlobalStyle>
            </ThemeProvider>
          </IntlProvider>
        </RootContext.Provider>
      );
    }
  }

  return keplerGlConnect(mapStateToProps, makeMapDispatchToProps)(withTheme(KeplerGL));
}