4 * Copyright 2016 RIFT.IO Inc
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 * https://github.com/larsmaultsby/react-rangeslider
23 * Forked from: https://github.com/whoisandie/react-rangeslider
28 Copyright (c) 2015 Bhargav Anand
30 Permission is hereby granted, free of charge, to any person obtaining a copy
31 of this software and associated documentation files (the "Software"), to deal
32 in the Software without restriction, including without limitation the rights
33 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34 copies of the Software, and to permit persons to whom the Software is
35 furnished to do so, subject to the following conditions:
37 The above copyright notice and this permission notice shall be included in all
38 copies or substantial portions of the Software.
40 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
50 import React, { PropTypes, Component, findDOMNode } from 'react';
51 import joinClasses from 'react/lib/joinClasses';
53 function capitalize(str) {
54 return str.charAt(0).toUpperCase() + str.substr(1);
57 function maxmin(pos, min, max) {
58 if (pos < min) { return min; }
59 if (pos > max) { return max; }
78 class Slider extends Component {
80 min: PropTypes.number,
81 max: PropTypes.number,
82 step: PropTypes.number,
83 value: PropTypes.number,
84 orientation: PropTypes.string,
85 onChange: PropTypes.func,
86 className: PropTypes.string,
89 static defaultProps = {
94 orientation: 'horizontal',
102 // Add window resize event listener here
103 componentDidMount() {
104 this.calculateDimensions();
105 window.addEventListener('resize', this.calculateDimensions);
108 componentWillUnmount() {
109 window.removeEventListener('resize', this.calculateDimensions);
112 handleSliderMouseDown = (e) => {
113 let value, { onChange } = this.props;
114 if (!onChange) return;
115 value = this.position(e);
116 onChange && onChange(value);
119 handleKnobMouseDown = () => {
120 document.addEventListener('mousemove', this.handleDrag);
121 document.addEventListener('mouseup', this.handleDragEnd);
124 handleDrag = (e) => {
125 let value, { onChange } = this.props;
126 if (!onChange) return;
127 value = this.position(e);
128 onChange && onChange(value);
131 handleDragEnd = () => {
132 document.removeEventListener('mousemove', this.handleDrag);
133 document.removeEventListener('mouseup', this.handleDragEnd);
136 handleNoop = (e) => {
141 calculateDimensions = () => {
142 let { orientation } = this.props;
143 let dimension = capitalize(constants.orientation[orientation].dimension);
144 const sliderPos = findDOMNode(this.refs.slider)['offset' + dimension];
145 const handlePos = findDOMNode(this.refs.handle)['offset' + dimension]
147 limit: sliderPos - handlePos,
151 getPositionFromValue = (value) => {
153 let { limit } = this.state;
154 let { min, max } = this.props;
155 percentage = (value - min) / (max - min);
156 pos = Math.round(percentage * limit);
161 getValueFromPosition = (pos) => {
162 let percentage, value;
163 let { limit } = this.state;
164 let { orientation, min, max, step } = this.props;
165 percentage = (maxmin(pos, 0, limit) / (limit || 1));
167 if (orientation === 'horizontal') {
168 value = step * Math.round(percentage * (max - min) / step) + min;
170 value = max - (step * Math.round(percentage * (max - min) / step) + min);
177 let pos, value, { grab } = this.state;
178 let { orientation } = this.props;
179 const node = findDOMNode(this.refs.slider);
180 const coordinateStyle = constants.orientation[orientation].coordinate;
181 const directionStyle = constants.orientation[orientation].direction;
182 const coordinate = e['client' + capitalize(coordinateStyle)];
183 const direction = node.getBoundingClientRect()[directionStyle];
185 pos = coordinate - direction - grab;
186 value = this.getValueFromPosition(pos);
191 coordinates = (pos) => {
192 let value, fillPos, handlePos;
193 let { limit, grab } = this.state;
194 let { orientation } = this.props;
195 value = this.getValueFromPosition(pos);
196 handlePos = this.getPositionFromValue(value);
198 if (orientation === 'horizontal') {
199 fillPos = handlePos + grab;
201 fillPos = limit - handlePos + grab;
211 let dimension, direction, position, coords, fillStyle, handleStyle, displayValue;
212 let { value, orientation, className } = this.props;
214 dimension = constants.orientation[orientation].dimension;
215 direction = constants.orientation[orientation].direction;
217 position = this.getPositionFromValue(value);
218 coords = this.coordinates(position);
220 fillStyle = {[dimension]: `${coords.fill}px`};
221 handleStyle = {[direction]: `${coords.handle}px`};
223 if(this.props.displayValue) {
224 displayValue = <div>{this.props.value}</div>;
230 className={joinClasses('rangeslider ', 'rangeslider-' + orientation, className)}
231 onMouseDown={this.handleSliderMouseDown}
232 onClick={this.handleNoop}
233 style={{display:'flex'}}>
236 className="rangeslider__fill"
240 className="rangeslider__handle"
241 onMouseDown={this.handleKnobMouseDown}
242 onClick={this.handleNoop}
251 export default Slider;