+
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+/**
+ * https://github.com/larsmaultsby/react-rangeslider
+ *
+ *
+ * Forked from: https://github.com/whoisandie/react-rangeslider
+ *
+ *
+ The MIT License (MIT)
+
+ Copyright (c) 2015 Bhargav Anand
+
+ 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, { PropTypes, Component, findDOMNode } from 'react';
+import joinClasses from 'react/lib/joinClasses';
+
+function capitalize(str) {
+ return str.charAt(0).toUpperCase() + str.substr(1);
+}
+
+function maxmin(pos, min, max) {
+ if (pos < min) { return min; }
+ if (pos > max) { return max; }
+ return pos;
+}
+
+const constants = {
+ orientation: {
+ horizontal: {
+ dimension: 'width',
+ direction: 'left',
+ coordinate: 'x',
+ },
+ vertical: {
+ dimension: 'height',
+ direction: 'top',
+ coordinate: 'y',
+ }
+ }
+};
+
+class Slider extends Component {
+ static propTypes = {
+ min: PropTypes.number,
+ max: PropTypes.number,
+ step: PropTypes.number,
+ value: PropTypes.number,
+ orientation: PropTypes.string,
+ onChange: PropTypes.func,
+ className: PropTypes.string,
+ }
+
+ static defaultProps = {
+ min: 0,
+ max: 100,
+ step: 1,
+ value: 0,
+ orientation: 'horizontal',
+ }
+
+ state = {
+ limit: 0,
+ grab: 0
+ }
+
+ // Add window resize event listener here
+ componentDidMount() {
+ this.calculateDimensions();
+ window.addEventListener('resize', this.calculateDimensions);
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('resize', this.calculateDimensions);
+ }
+
+ handleSliderMouseDown = (e) => {
+ let value, { onChange } = this.props;
+ if (!onChange) return;
+ value = this.position(e);
+ onChange && onChange(value);
+ }
+
+ handleKnobMouseDown = () => {
+ document.addEventListener('mousemove', this.handleDrag);
+ document.addEventListener('mouseup', this.handleDragEnd);
+ }
+
+ handleDrag = (e) => {
+ let value, { onChange } = this.props;
+ if (!onChange) return;
+ value = this.position(e);
+ onChange && onChange(value);
+ }
+
+ handleDragEnd = () => {
+ document.removeEventListener('mousemove', this.handleDrag);
+ document.removeEventListener('mouseup', this.handleDragEnd);
+ }
+
+ handleNoop = (e) => {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ calculateDimensions = () => {
+ let { orientation } = this.props;
+ let dimension = capitalize(constants.orientation[orientation].dimension);
+ const sliderPos = findDOMNode(this.refs.slider)['offset' + dimension];
+ const handlePos = findDOMNode(this.refs.handle)['offset' + dimension]
+ this.setState({
+ limit: sliderPos - handlePos,
+ grab: handlePos / 2,
+ });
+ }
+ getPositionFromValue = (value) => {
+ let percentage, pos;
+ let { limit } = this.state;
+ let { min, max } = this.props;
+ percentage = (value - min) / (max - min);
+ pos = Math.round(percentage * limit);
+
+ return pos;
+ }
+
+ getValueFromPosition = (pos) => {
+ let percentage, value;
+ let { limit } = this.state;
+ let { orientation, min, max, step } = this.props;
+ percentage = (maxmin(pos, 0, limit) / (limit || 1));
+
+ if (orientation === 'horizontal') {
+ value = step * Math.round(percentage * (max - min) / step) + min;
+ } else {
+ value = max - (step * Math.round(percentage * (max - min) / step) + min);
+ }
+
+ return value;
+ }
+
+ position = (e) => {
+ let pos, value, { grab } = this.state;
+ let { orientation } = this.props;
+ const node = findDOMNode(this.refs.slider);
+ const coordinateStyle = constants.orientation[orientation].coordinate;
+ const directionStyle = constants.orientation[orientation].direction;
+ const coordinate = e['client' + capitalize(coordinateStyle)];
+ const direction = node.getBoundingClientRect()[directionStyle];
+
+ pos = coordinate - direction - grab;
+ value = this.getValueFromPosition(pos);
+
+ return value;
+ }
+
+ coordinates = (pos) => {
+ let value, fillPos, handlePos;
+ let { limit, grab } = this.state;
+ let { orientation } = this.props;
+ value = this.getValueFromPosition(pos);
+ handlePos = this.getPositionFromValue(value);
+
+ if (orientation === 'horizontal') {
+ fillPos = handlePos + grab;
+ } else {
+ fillPos = limit - handlePos + grab;
+ }
+
+ return {
+ fill: fillPos,
+ handle: handlePos,
+ };
+ }
+
+ render() {
+ let dimension, direction, position, coords, fillStyle, handleStyle, displayValue;
+ let { value, orientation, className } = this.props;
+
+ dimension = constants.orientation[orientation].dimension;
+ direction = constants.orientation[orientation].direction;
+
+ position = this.getPositionFromValue(value);
+ coords = this.coordinates(position);
+
+ fillStyle = {[dimension]: `${coords.fill}px`};
+ handleStyle = {[direction]: `${coords.handle}px`};
+
+ if(this.props.displayValue) {
+ displayValue = <div>{this.props.value}</div>;
+ }
+
+ return (
+ <div
+ ref="slider"
+ className={joinClasses('rangeslider ', 'rangeslider-' + orientation, className)}
+ onMouseDown={this.handleSliderMouseDown}
+ onClick={this.handleNoop}
+ style={{display:'flex'}}>
+ <div
+ ref="fill"
+ className="rangeslider__fill"
+ style={fillStyle} />
+ <div
+ ref="handle"
+ className="rangeslider__handle"
+ onMouseDown={this.handleKnobMouseDown}
+ onClick={this.handleNoop}
+ style={handleStyle}>
+ {displayValue}
+ </div>
+ </div>
+ );
+ }
+}
+
+export default Slider;