UNPKG

kepler.gl.geoiq

Version:

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

300 lines (272 loc) 9.75 kB
// Copyright (c) 2019 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, {Component} from 'react'; import styled from 'styled-components'; import PropTypes from 'prop-types'; import {sortable} from 'react-anything-sortable'; import _ from 'lodash'; import WidgetConfigurator from './widget-configurator'; import WidgetPanelHeader from './widget-panel-header'; import LoadingSpinner from 'components/common/loading-spinner'; import SliderHandle from 'components/common/slider/slider-bar-handle'; // import layers from '../../../../dist/components/common/icons/layers'; const PanelWrapper = styled.div` font-size: 12px; border-radius: 1px; margin-bottom: 8px; &.dragging { cursor: move; } `; const StyledWidgetContent = styled.div` background-color: ${props => props.theme.panelBackground}; padding: 12px; `; const StyledWidgetContentHeader = styled.div` background-color: ${props => props.theme.panelBackground}; color: #6a7485; text-align: center; font-size: 14px; `; const StyledWidgetContentValue = styled.div` background-color: ${props => props.theme.panelBackground}; text-align: center; font-size: 30px; `; function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); } function WidgetPanelFactory() { @sortable class WidgetPanel extends Component { static propTypes = { widget: PropTypes.object.isRequired, datasets: PropTypes.object.isRequired, idx: PropTypes.number.isRequired, widgetConfigChange: PropTypes.func.isRequired, widgetTypeChange: PropTypes.func.isRequired, // layers: propTypes.arrayOf(propTypes.any), openModal: PropTypes.func.isRequired, removeWidget: PropTypes.func.isRequired, onCloseConfig: PropTypes.func, widgetTypeOptions: PropTypes.arrayOf(PropTypes.any), widgetVisConfigChange: PropTypes.func, widgetVisualChannelConfigChange: PropTypes.func, mapState: PropTypes.object.isRequired }; state = { aggregatedData: null }; inDebounce = 0; componentDidMount() { this.props.widgetConfigChange(this.props.widget, { mapState: this.props.mapState }); } componentWillReceiveProps(nextProps) { if ( !_.isEqual(this.props.mapState, nextProps.mapState) && nextProps.layers && nextProps.widget.config.bounds ) { this.props.widgetConfigChange(this.props.widget, { isCalculating: true }); clearTimeout(this.inDebounce); this.inDebounce = setTimeout(() => { this.props.widgetConfigChange(this.props.widget, { mapState: nextProps.mapState }); clearTimeout(this.inDebounce); this.inDebounce = 0; }, 300); } } updateState = func => { this.setState({aggregatedData: func}); }; updateWidgetConfig = newProp => { newProp = { ...newProp, mapState: this.props.mapState }; this.props.widgetConfigChange(this.props.widget, newProp); }; updateWidgetType = newType => { this.props.widgetTypeChange(this.props.widget, newType); }; updateWidgetVisConfig = newVisConfig => { this.props.widgetVisConfigChange(this.props.widget, newVisConfig); }; updateWidgetVisualChannelConfig = (newConfig, channel, scaleKey) => { this.props.widgetVisualChannelConfigChange( this.props.widget, newConfig, channel, scaleKey ); }; _updateWidgetLabel = ({target: {value}}) => { this.updateWidgetConfig({label: value}); }; _toggleVisibility = e => { e.stopPropagation(); const isVisible = !this.props.widget.config.isVisible; this.updateWidgetConfig({isVisible}); }; _toggleEnableConfig = e => { e.stopPropagation(); const { widget: { config: {isConfigActive} } } = this.props; this.updateWidgetConfig({isConfigActive: !isConfigActive}); }; _removeWidget = e => { e.stopPropagation(); this.props.removeWidget(this.props.idx); }; _renderLayerName(id) { var placeholder = this.props.layers.map(l => { if (l.id === id) { return l.config.label; } }); return placeholder; } render() { const {widget, layers, idx, datasets, widgetTypeOptions} = this.props; const {config} = widget; const {isConfigActive} = config; return ( <PanelWrapper active={isConfigActive} className={`layer-panel ${this.props.className}`} style={this.props.style} onMouseDown={this.props.onMouseDown} onTouchStart={this.props.onTouchStart} > <WidgetPanelHeader isConfigActive={isConfigActive} id={widget.id} idx={idx} isVisible={config.isVisible} label={config.label} labelRCGColorValues={datasets[config.dataId].color} widgetType={widget.name} onToggleEnableConfig={this._toggleEnableConfig} onToggleVisibility={this._toggleVisibility} onUpdateWidgetLabel={this._updateWidgetLabel} onRemoveWidget={this._removeWidget} /> {isConfigActive && ( <WidgetConfigurator widget={widget} layers={layers} datasets={datasets} widgetTypeOptions={widgetTypeOptions} openModal={this.props.openModal} updateLayerConfig={this.updateWidgetConfig} updateLayerVisualChannelConfig={ this.updateWidgetVisualChannelConfig } updateLayerType={this.updateWidgetType} updateLayerVisConfig={this.updateWidgetVisConfig} /> )} {(widget.type === 'Function' && config.isVisible && config.aggregatedData && config.aggregationType && typeof config.aggregatedData !== 'object') || config.aggregatedData === 0 ? ( <StyledWidgetContent> <StyledWidgetContentHeader> {capitalizeFirstLetter(config.aggregationType)} :{' '} {this._renderLayerName(config.selectedLayer.id)} :{' '} {config.fieldName} </StyledWidgetContentHeader> <StyledWidgetContentValue> {config.isCalculating ? ( <div style={{display: 'inline-block', marginTop: '5px'}}> <LoadingSpinner /> </div> ) : ( <div style={{color: '#6a7485'}}>{config.aggregatedData}</div> )} </StyledWidgetContentValue> </StyledWidgetContent> ) : null} {widget.type === 'Category' && config.isVisible && config.aggregatedData && config.aggregatedData.length && config.aggregationType ? ( <StyledWidgetContent> {config.aggregatedData.map(ad => { return ( <React.Fragment> <StyledWidgetContentHeader style={{textAlign: 'inherit'}}> <div style={{display: 'flex', color: '#6a7485'}}> {ad['y']}{' '} <div style={{marginLeft: 'auto'}}>{ad['x']}</div> </div> </StyledWidgetContentHeader> <SliderHandle type={'category'} width={(ad['x'] / config.aggregatedData[0]['x']) * 100} sliderBarListener={null} /> <StyledWidgetContentValue /> </React.Fragment> ); })} </StyledWidgetContent> ) : null} </PanelWrapper> ); } } return WidgetPanel; } export default WidgetPanelFactory; { /* <StyledWidgetContent> {config.aggregatedData.map(ad => { return ( <React.Fragment> <StyledWidgetContentHeader style={{textAlign: 'inherit'}}> {ad[0]}-{ad[1]} </StyledWidgetContentHeader> <StyledWidgetContentValue /> </React.Fragment> ); })} </StyledWidgetContent> */ } { /* <WidgetBarChart data={config.aggregatedData} height={96.9} width={191.6} margin={0} /> */ }