kepler.gl.geoiq
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
195 lines (176 loc) • 6 kB
JavaScript
// Copyright (c) 2023 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 PropTypes from 'prop-types';
import Sortable from 'react-anything-sortable';
import styled from 'styled-components';
import {createSelector} from 'reselect';
import WidgetPanelFactory from './widget-panel/widget-panel';
import SourceDataCatalogFactory from './common/source-data-catalog';
import {Add} from 'components/common/icons';
import {
SidePanelDivider,
SidePanelSection,
Button
} from 'components/common/styled-components';
const StyledSortable = styled.div`
.ui-sortable {
display: block;
position: relative;
overflow: visible;
user-select: none;
:before {
content: ' ';
display: table;
}
:after {
content: ' ';
display: table;
}
}
.ui-sortable-item.ui-sortable-dragging {
position: absolute;
z-index: 1688;
cursor: move;
}
.ui-sortable-item.ui-sortable-dragging:hover {
cursor: move;
opacity: 0.5;
}
.ui-sortable-placeholder {
display: none;
}
.ui-sortable-placeholder.visible {
display: block;
opacity: 0;
z-index: -1;
}
`;
WidgetManagerFactory.deps = [WidgetPanelFactory, SourceDataCatalogFactory];
function WidgetManagerFactory(WidgetPanel, SourceDataCatalog) {
return class WidgetManager extends Component {
static propTypes = {
addWidget: PropTypes.func.isRequired,
datasets: PropTypes.object.isRequired,
widgetClasses: PropTypes.object.isRequired,
widgets: PropTypes.arrayOf(PropTypes.any).isRequired,
widgetConfigChange: PropTypes.func.isRequired,
widgetVisualChannelConfigChange: PropTypes.func.isRequired,
widgetTypeChange: PropTypes.func.isRequired,
widgetVisConfigChange: PropTypes.func.isRequired,
// layers: propTypes.arrayOf(propTypes.any),
openModal: PropTypes.func.isRequired,
removeWidget: PropTypes.func.isRequired,
showDatasetTable: PropTypes.func.isRequired,
updateWidgetOrder: PropTypes.func.isRequired,
mapState: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
project: PropTypes.object.isRequired
};
widgetClassSelector = props => props.widgetClasses;
widgetTypeOptionsSelector = createSelector(
this.widgetClassSelector,
widgetClasses =>
Object.keys(widgetClasses).map(key => {
const widget = new widgetClasses[key]();
return {
id: key,
label: widget.name,
icon: widget.widgetIcon
};
})
);
_addEmptyNewWidget = () => {
this.props.addWidget();
};
_handleSort = order => {
this.props.updateWidgetOrder(order);
};
render() {
const {
widgets,
layers,
datasets,
widgetOrder,
openModal,
mapState,
filters,
auth,
project
} = this.props;
const hadDataset = Object.keys(datasets).length;
const hadEmptyWidget = widgets.some(w => !w.name);
const widgetTypeOptions = this.widgetTypeOptionsSelector(this.props);
const widgetActions = {
widgetConfigChange: this.props.widgetConfigChange,
widgetVisualChannelConfigChange: this.props
.widgetVisualChannelConfigChange,
widgetTypeChange: this.props.widgetTypeChange,
widgetVisConfigChange: this.props.widgetVisConfigChange,
removeWidget: this.props.removeWidget
};
const panelProps = {datasets, openModal, widgetTypeOptions};
return (
<StyledSortable className="widget-manager">
<SourceDataCatalog
datasets={datasets}
showDatasetTable={this.props.showDatasetTable}
/>
<SidePanelDivider />
<SidePanelSection>
<Sortable
onSort={this._handleSort}
direction="vertical"
sortHandle="sort--handle"
dynamic
>
{widgetOrder.map(idx => (
<WidgetPanel
{...panelProps}
{...widgetActions}
sortData={idx}
key={widgets[idx].id}
idx={idx}
widget={widgets[idx]}
layers={layers}
filters={filters}
mapState={mapState}
auth={auth}
project={project}
/>
))}
</Sortable>
</SidePanelSection>
<SidePanelSection>
<Button
inactive={hadEmptyWidget || !hadDataset}
onClick={this._addEmptyNewWidget}
width="auto"
>
<Add height="12px" />
Add Widget
</Button>
</SidePanelSection>
</StyledSortable>
);
}
};
}
export default WidgetManagerFactory;