UNPKG

kepler.gl

Version:

kepler.gl is a webgl based application to visualize large scale location data in the browser

316 lines (287 loc) 11.1 kB
// 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, {PureComponent} from 'react'; import {FormattedMessage} from 'react-intl'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import get from 'lodash.get'; import SidebarFactory from './side-panel/side-bar'; import PanelHeaderFactory from './side-panel/panel-header'; import LayerManagerFactory from './side-panel/layer-manager'; import FilterManagerFactory from './side-panel/filter-manager'; import InteractionManagerFactory from './side-panel/interaction-manager'; import MapManagerFactory from './side-panel/map-manager'; import PanelToggleFactory from './side-panel/panel-toggle'; import CustomPanelsFactory from './side-panel/custom-panel'; import { ADD_DATA_ID, ADD_MAP_STYLE_ID, DATA_TABLE_ID, EXPORT_IMAGE_ID, EXPORT_DATA_ID, EXPORT_MAP_ID, SAVE_MAP_ID, SHARE_MAP_ID, SIDEBAR_PANELS, OVERWRITE_MAP_ID } from 'constants/default-settings'; const SidePanelContent = styled.div` ${props => props.theme.sidePanelScrollBar}; flex-grow: 1; padding: ${props => props.theme.sidePanelInnerPadding}px; overflow-y: scroll; overflow-x: hidden; `; export const PanelTitleFactory = () => styled.div` color: ${props => props.theme.titleTextColor}; font-size: 20px; font-weight: 400; letter-spacing: 1.25px; margin-bottom: 14px; `; SidePanelFactory.deps = [ SidebarFactory, PanelHeaderFactory, PanelToggleFactory, PanelTitleFactory, LayerManagerFactory, FilterManagerFactory, InteractionManagerFactory, MapManagerFactory, CustomPanelsFactory ]; /** * * Vertical sidebar containing input components for the rendering layers */ export default function SidePanelFactory( Sidebar, PanelHeader, PanelToggle, PanelTitle, LayerManager, FilterManager, InteractionManager, MapManager, CustomPanels ) { const customPanels = get(CustomPanels, ['defaultProps', 'panels']) || []; const getCustomPanelProps = get(CustomPanels, ['defaultProps', 'getProps']) || (() => ({})); class SidePanel extends PureComponent { static propTypes = { filters: PropTypes.arrayOf(PropTypes.any).isRequired, interactionConfig: PropTypes.object.isRequired, layerBlending: PropTypes.string.isRequired, layers: PropTypes.arrayOf(PropTypes.any).isRequired, layerClasses: PropTypes.object.isRequired, mapStyle: PropTypes.object.isRequired, width: PropTypes.number.isRequired, datasets: PropTypes.object.isRequired, visStateActions: PropTypes.object.isRequired, mapStyleActions: PropTypes.object.isRequired, availableProviders: PropTypes.object, mapSaved: PropTypes.string, panels: PropTypes.arrayOf(PropTypes.object) }; static defaultProps = { panels: SIDEBAR_PANELS, uiState: {}, visStateActions: {}, mapStyleActions: {}, uiStateActions: {}, availableProviders: {} }; /* component private functions */ _onOpenOrClose = () => { this.props.uiStateActions.toggleSidePanel( this.props.uiState.activeSidePanel ? null : 'layer' ); }; _showDatasetTable = dataId => { // this will open data table modal this.props.visStateActions.showDatasetTable(dataId); this.props.uiStateActions.toggleModal(DATA_TABLE_ID); }; _showAddDataModal = () => { this.props.uiStateActions.toggleModal(ADD_DATA_ID); }; _showAddMapStyleModal = () => { this.props.uiStateActions.toggleModal(ADD_MAP_STYLE_ID); }; _removeDataset = key => { // this will show the modal dialog to confirm deletion this.props.uiStateActions.openDeleteModal(key); }; _onClickExportImage = () => this.props.uiStateActions.toggleModal(EXPORT_IMAGE_ID); _onClickExportData = () => this.props.uiStateActions.toggleModal(EXPORT_DATA_ID); _onClickExportMap = () => this.props.uiStateActions.toggleModal(EXPORT_MAP_ID); _onClickSaveToStorage = () => { this.props.uiStateActions.toggleModal(this.props.mapSaved ? OVERWRITE_MAP_ID : SAVE_MAP_ID); }; _onClickSaveAsToStorage = () => { // add (copy) to file name this.props.visStateActions.setMapInfo({ title: `${this.props.mapInfo.title || 'Kepler.gl'} (Copy)` }); this.props.uiStateActions.toggleModal(SAVE_MAP_ID); }; _onClickShareMap = () => this.props.uiStateActions.toggleModal(SHARE_MAP_ID); // eslint-disable-next-line complexity render() { const { appName, appWebsite, version, datasets, filters, layers, layerBlending, layerClasses, uiState, layerOrder, interactionConfig, visStateActions, mapStyleActions, uiStateActions, availableProviders } = this.props; const {activeSidePanel} = uiState; const isOpen = Boolean(activeSidePanel); const panels = [...this.props.panels, ...customPanels]; const layerManagerActions = { addLayer: visStateActions.addLayer, layerConfigChange: visStateActions.layerConfigChange, layerColorUIChange: visStateActions.layerColorUIChange, layerTextLabelChange: visStateActions.layerTextLabelChange, layerVisualChannelConfigChange: visStateActions.layerVisualChannelConfigChange, layerTypeChange: visStateActions.layerTypeChange, layerVisConfigChange: visStateActions.layerVisConfigChange, updateLayerBlending: visStateActions.updateLayerBlending, updateLayerOrder: visStateActions.reorderLayer, showDatasetTable: this._showDatasetTable, showAddDataModal: this._showAddDataModal, removeLayer: visStateActions.removeLayer, removeDataset: this._removeDataset, openModal: uiStateActions.toggleModal }; const filterManagerActions = { addFilter: visStateActions.addFilter, removeFilter: visStateActions.removeFilter, setFilter: visStateActions.setFilter, showDatasetTable: this._showDatasetTable, showAddDataModal: this._showAddDataModal, toggleAnimation: visStateActions.toggleFilterAnimation, enlargeFilter: visStateActions.enlargeFilter, toggleFilterFeature: visStateActions.toggleFilterFeature }; const interactionManagerActions = { onConfigChange: visStateActions.interactionConfigChange }; const mapManagerActions = { addMapStyleUrl: mapStyleActions.addMapStyleUrl, onConfigChange: mapStyleActions.mapConfigChange, onStyleChange: mapStyleActions.mapStyleChange, onBuildingChange: mapStyleActions.mapBuildingChange, set3dBuildingColor: mapStyleActions.set3dBuildingColor, showAddMapStyleModal: this._showAddMapStyleModal }; return ( <div> <Sidebar width={this.props.width} isOpen={isOpen} minifiedWidth={0} onOpenOrClose={this._onOpenOrClose} > <PanelHeader appName={appName} version={version} appWebsite={appWebsite} visibleDropdown={uiState.visibleDropdown} showExportDropdown={uiStateActions.showExportDropdown} hideExportDropdown={uiStateActions.hideExportDropdown} onExportImage={this._onClickExportImage} onExportData={this._onClickExportData} onExportMap={this._onClickExportMap} onSaveMap={this.props.onSaveMap} onSaveToStorage={availableProviders.hasStorage ? this._onClickSaveToStorage : null} onSaveAsToStorage={ availableProviders.hasStorage && this.props.mapSaved ? this._onClickSaveAsToStorage : null } onShareMap={availableProviders.hasShare ? this._onClickShareMap : null} /> <PanelToggle panels={panels} activePanel={activeSidePanel} togglePanel={uiStateActions.toggleSidePanel} /> <SidePanelContent className="side-panel__content"> <div> <PanelTitle className="side-panel__content__title"> <FormattedMessage id={(panels.find(({id}) => id === activeSidePanel) || {}).label} /> </PanelTitle> {activeSidePanel === 'layer' && ( <LayerManager {...layerManagerActions} datasets={datasets} layers={layers} layerClasses={layerClasses} layerOrder={layerOrder} layerBlending={layerBlending} colorPalette={uiState.colorPalette} /> )} {activeSidePanel === 'filter' && ( <FilterManager {...filterManagerActions} datasets={datasets} layers={layers} filters={filters} /> )} {activeSidePanel === 'interaction' && ( <InteractionManager {...interactionManagerActions} datasets={datasets} interactionConfig={interactionConfig} /> )} {activeSidePanel === 'map' && ( <MapManager {...mapManagerActions} mapStyle={this.props.mapStyle} /> )} {(customPanels || []).find(p => p.id === activeSidePanel) ? ( <CustomPanels {...getCustomPanelProps(this.props)} activeSidePanel={activeSidePanel} /> ) : null} </div> </SidePanelContent> </Sidebar> </div> ); } } return SidePanel; }