UNPKG

kepler.gl

Version:

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

210 lines (186 loc) 6.45 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, {Component, createRef} from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import styled from 'styled-components'; import SliderHandle from './slider-handle'; import SliderBarHandle from './slider-bar-handle'; import {roundValToStep} from 'utils/data-utils'; function noop() {} const StyledRangeSlider = styled.div` position: relative; margin-bottom: 12px; background-color: ${props => props.theme.sliderBarBgd}; ${props => `${props.vertical ? 'width' : 'height'}: ${props.theme.sliderBarHeight}px`}; ${props => `${props.vertical ? 'height' : 'width'}: 100%`}; `; const SliderWrapper = styled.div` flex-grow: 1; margin-top: ${props => props.isRanged ? props.theme.sliderMarginTopIsRange : props.theme.sliderMarginTop}px; `; export default class Slider extends Component { static propTypes = { title: PropTypes.string, isRanged: PropTypes.bool, value0: PropTypes.number, value1: PropTypes.number, minValue: PropTypes.number, maxValue: PropTypes.number, sliderHandleWidth: PropTypes.number, onSlider0Change: PropTypes.func, onInput0Change: PropTypes.func, onSlider1Change: PropTypes.func, onInput1Change: PropTypes.func, onSliderBarChange: PropTypes.func, step: PropTypes.number, enableBarDrag: PropTypes.bool, showTooltip: PropTypes.bool }; static defaultProps = { title: '', isRanged: true, value0: 0, value1: 100, minValue: 0, maxValue: 100, step: 1, sliderHandleWidth: 12, enableBarDrag: false, onSlider0Change: noop, onInput0Change: noop, onSlider1Change: noop, onInput1Change: noop, onSliderBarChange: noop, disabled: false, vertical: false, showTooltip: false }; ref = createRef(); _getBaseDistance() { return this.props.vertical ? this.ref.current.offsetHeight : this.ref.current.offsetWidth; } _getValDelta(x) { const percent = x / this._getBaseDistance(); const maxDelta = this.props.maxValue - this.props.minValue; return percent * maxDelta; } _getValue(val, offset) { const delta = this._getValDelta(offset); const rawValue = this.props.vertical ? val - delta : val + delta; return this._roundValToStep(rawValue); } _isVal0InRange = val => { const {value1, minValue} = this.props; return Boolean(val >= minValue && val <= value1); }; _isVal1InRange = val => { const {maxValue, value0} = this.props; return Boolean(val <= maxValue && val >= value0); }; _roundValToStep(val) { const {minValue, step} = this.props; return roundValToStep(minValue, step, val); } slide0Listener = x => { const val = this._getValue(this.props.value0, x); if (this._isVal0InRange(val)) { this.props.onSlider0Change(val); } }; slide1Listener = x => { const val = this._getValue(this.props.value1, x); if (this._isVal1InRange(val)) { this.props.onSlider1Change(val); } }; sliderBarListener = x => { const val0 = this._getValue(this.props.value0, x); const val1 = this._getValue(this.props.value1, x); if (this._isVal1InRange(val1) && this._isVal0InRange(val0)) { this.props.onSliderBarChange(val0, val1); } }; calcHandleLeft0 = (w, l, num) => { return w === 0 ? `calc(${l}% - ${this.props.sliderHandleWidth / 2}px)` : `calc(${l}% - ${this.props.sliderHandleWidth / 2}px)`; }; calcHandleLeft1 = (w, l) => { return this.props.isRanged && w === 0 ? `${l}%` : `calc(${l + w}% - ${this.props.sliderHandleWidth / 2}px)`; }; render() { const { classSet, disabled, isRanged, maxValue, minValue, value1, vertical, sliderHandleWidth, showTooltip } = this.props; const value0 = !isRanged && minValue > 0 ? minValue : this.props.value0; const currValDelta = value1 - value0; const maxDelta = maxValue - minValue; const width = (currValDelta / maxDelta) * 100; const v0Left = ((value0 - minValue) / maxDelta) * 100; return ( <SliderWrapper className={classnames('kg-slider', {...classSet, disabled})} ref={this.ref} isRanged={isRanged} vertical={vertical} > <StyledRangeSlider className="kg-range-slider" vertical={vertical}> <SliderHandle className="kg-range-slider__handle" left={this.calcHandleLeft0(width, v0Left)} valueListener={this.slide0Listener} sliderHandleWidth={sliderHandleWidth} display={isRanged} vertical={vertical} showTooltip={showTooltip} /> <SliderHandle className="kg-range-slider__handle" left={this.calcHandleLeft1(width, v0Left)} valueListener={this.slide1Listener} sliderHandleWidth={sliderHandleWidth} vertical={vertical} value={value1} showTooltip={showTooltip} /> <SliderBarHandle width={width} v0Left={v0Left} enableBarDrag={this.props.enableBarDrag} sliderBarListener={this.sliderBarListener} vertical={vertical} /> </StyledRangeSlider> </SliderWrapper> ); } }