kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
235 lines (217 loc) • 6.63 kB
JavaScript
// Copyright (c) 2018 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 uniq from 'lodash.uniq';
import styled from 'styled-components';
import ItemSelector from 'components/common/item-selector/item-selector';
import {PanelLabel} from 'components/common/styled-components';
import RangeSlider from 'components/common/range-slider';
import Switch from 'components/common/switch';
import ColorPalette from './color-palette';
import {COLOR_RANGES} from 'constants/color-ranges';
const ALL_TYPES = uniq(COLOR_RANGES.map(c => c.type).concat(['all']));
const ALL_STEPS = uniq(COLOR_RANGES.map(d => d.colors.length));
const StyledColorConfig = styled.div`
padding: 12px 12px 0 12px;
`;
const ColorRangeSelector = styled.div`
padding-bottom: 12px;
`;
export default class ColorRangeSelect extends Component {
static propTypes = {
colorRanges: PropTypes.arrayOf(PropTypes.any),
selectedColorRange: PropTypes.object,
onSelectColorRange: PropTypes.func.isRequired
};
static defaultProps = {
colorRanges: COLOR_RANGES,
onSelectColorRange: () => {}
};
state = {
config: {
type: {
type: 'select',
value: 'all',
options: ALL_TYPES
},
steps: {
type: 'select',
value: 6,
options: ALL_STEPS
},
reversed: {
type: 'switch',
value: false,
options: [true, false]
}
}
};
_updateConfig = ({key, value}) => {
const currentValue = this.state.config[key].value;
if (value !== currentValue) {
this.setState({
config: {
...this.state.config,
[key]: {
...this.state.config[key],
value
}
}
});
}
};
render() {
const {config} = this.state;
return (
<ColorRangeSelector className="color-range-selector">
<StyledColorConfig>
{Object.keys(config).map(key => (
<PaletteConfig
key={key}
label={key}
config={config[key]}
onChange={value => this._updateConfig({key, value})}
/>
))}
</StyledColorConfig>
<ColorPaletteGroup
config={config}
colorRanges={this.props.colorRanges}
onSelect={this.props.onSelectColorRange}
selected={this.props.selectedColorRange}
/>
</ColorRangeSelector>
);
}
}
const StyledPaletteConfig = styled.div`
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
.color-palette__config__label {
flex-grow: 1;
}
.color-palette__config__select {
flex-grow: 1;
}
.item-selector .item-selector__dropdown {
${props => props.theme.secondaryInput};
}
`;
const PaletteConfig = ({
category,
label,
config: {type, value, options},
onChange
}) => (
<StyledPaletteConfig
className="color-palette__config"
onClick={e => e.stopPropagation()}
>
<div className="color-palette__config__label">
<PanelLabel>{label}</PanelLabel>
</div>
{type === 'select' && (
<div className="color-palette__config__select">
<ItemSelector
selectedItems={value}
options={options}
multiSelect={false}
searchable={false}
onChange={onChange}
/>
</div>
)}
{type === 'slider' && (
<div className="color-palette__config__slider">
<div className="color-palette__config__slider__slider">
<RangeSlider
range={options}
value0={options[0]}
value1={value}
step={1}
isRanged={false}
showInput={false}
onChange={val => onChange(val[1])}
/>
</div>
<div className="color-palette__config__slider__number">{value}</div>
</div>
)}
{type === 'switch' && (
<Switch
checked={value}
id={`${category}-${label}-toggle`}
onChange={() => onChange(!value)}
secondary
/>
)}
</StyledPaletteConfig>
);
const StyledColorRange = styled.div`
padding: 0 8px;
:hover {
background-color: ${props => props.theme.panelBackgroundHover};
cursor: pointer;
}
`;
const ColorPaletteGroup = ({config = {}, onSelect, selected, colorRanges}) => {
const {steps, reversed, type} = config;
const filtered = colorRanges.filter(colorRange => {
const isType =
!type || type.value === 'all' || type.value === colorRange.type;
const isStep = !steps || Number(steps.value) === colorRange.colors.length;
return isType && isStep;
});
const isReversed = Boolean(reversed && reversed.value);
return (
<div className="color-palette__group">
{filtered.map(colorRange => (
<StyledColorRange
className="color-ranges"
key={colorRange.name}
onClick={e =>
onSelect(
{
...colorRange,
reversed: isReversed,
colors: isReversed
? colorRange.colors.slice().reverse()
: colorRange.colors
},
e
)
}
>
<ColorPalette
colors={colorRange.colors}
isReversed={isReversed}
isSelected={
colorRange.name === selected.name &&
isReversed === Boolean(selected.reversed)
}
/>
</StyledColorRange>
))}
</div>
);
};