🚀 Big News:Socket Has Acquired Secure Annex.Learn More
Socket
Book a DemoSign in
Socket

react-slider

Package Overview
Dependencies
Maintainers
5
Versions
54
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-slider - npm Package Compare versions

Comparing version
0.11.2
to
1.0.0-0
+52
CHANGELOG.md
# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
# [1.0.0-0](https://github.com/zillow/react-slider/compare/v0.11.2...v1.0.0-0) (2019-09-27)
### Bug Fixes
* `onAfterChange` was not being fired ([f8697c7](https://github.com/zillow/react-slider/commit/f8697c7)), closes [#3](https://github.com/zillow/react-slider/issues/3)
* add IE11 arrow key support ([f6b9b1f](https://github.com/zillow/react-slider/commit/f6b9b1f))
* aria keyboard support corrected so that left and down decreases and right and up increases ([4e21bab](https://github.com/zillow/react-slider/commit/4e21bab)), closes [#144](https://github.com/zillow/react-slider/issues/144)
* do nothing when right clicking on the slider ([85c55c9](https://github.com/zillow/react-slider/commit/85c55c9))
* Home and End keys no longer scroll the page when setting the value ([9830bc9](https://github.com/zillow/react-slider/commit/9830bc9))
* remove active state on thumb on blur ([8818d3b](https://github.com/zillow/react-slider/commit/8818d3b))
* the active thumb should get focus when a value is selected ([66d2533](https://github.com/zillow/react-slider/commit/66d2533))
### Code Refactoring
* remove support for custom thumbs via `children` ([99b0c1a](https://github.com/zillow/react-slider/commit/99b0c1a))
### Features
* "handle" and "bar" are now "thumb" and "track" to follow `input[type=range]` nomenclature ([87b1b4b](https://github.com/zillow/react-slider/commit/87b1b4b))
* `ariaValuetext` now supports a function for dynamic value text ([f465ac6](https://github.com/zillow/react-slider/commit/f465ac6))
* `renderThumb` now renders the entire thumb node rather than just the thumb content ([324dfba](https://github.com/zillow/react-slider/commit/324dfba))
* `withTracks` is now true by default ([51ab37e](https://github.com/zillow/react-slider/commit/51ab37e))
* add `renderBar` render prop for customizing bar content ([25925b6](https://github.com/zillow/react-slider/commit/25925b6))
* add `renderHandle` render prop for dynamic handle content ([5ec7350](https://github.com/zillow/react-slider/commit/5ec7350))
* add `valueNow` to state objects of `ariaValuetext` and `renderThumb` for easier access to the current value ([48b1662](https://github.com/zillow/react-slider/commit/48b1662))
* add aria-orientation to slider ([6c8ef00](https://github.com/zillow/react-slider/commit/6c8ef00))
* add support for Page Up and Page Down keys ([d8b881d](https://github.com/zillow/react-slider/commit/d8b881d))
* pass `value` state to render props ([0a81b50](https://github.com/zillow/react-slider/commit/0a81b50))
* remove dependency on create-react-class ([494f21e](https://github.com/zillow/react-slider/commit/494f21e))
* the paging value is now configurable via the `pageFn` prop ([cf34e34](https://github.com/zillow/react-slider/commit/cf34e34))
### BREAKING CHANGES
* The render props `renderThumb` and `renderTrack` are now passed two arguments
instead of one, `props` and `state`. This makes it easier to just spread props when using
a render function.
* `renderThumb` was previously given the indexed handle value
which now needs to be derived from `value` and `index.
* custom thumbs via `children` is no longer supported.
To customize thumbs, use the `renderThumb` render prop instead.
* `withTracks` is more commonly true than false,
so we are making that the default
* "handle" and "bar" props have been renamed to "thumb" and "track",
e.g. `withBars` is now `withTracks`, and `handleClassName` is now `thumbClassName`

Sorry, the diff of this file is not supported yet

var _jsxFileName = "/Users/brians/git/react-slider/src/components/ReactSlider/__tests__/ReactSlider.test.js";
import React from 'react';
import renderer from 'react-test-renderer';
import ReactSlider from '../ReactSlider';
describe('<ReactSlider>', function () {
it('can render', function () {
var tree = renderer.create(React.createElement(ReactSlider, {
__source: {
fileName: _jsxFileName,
lineNumber: 7
},
__self: this
})).toJSON();
expect(tree).toMatchSnapshot();
});
it('should call onAfterChange callback when onEnd is called', function () {
var onAfterChange = jest.fn();
var testRenderer = renderer.create(React.createElement(ReactSlider, {
onAfterChange: onAfterChange,
__source: {
fileName: _jsxFileName,
lineNumber: 13
},
__self: this
}));
var testInstance = testRenderer.root;
expect(onAfterChange).not.toHaveBeenCalled();
testInstance.instance.onBlur();
expect(onAfterChange).toHaveBeenCalledTimes(1);
});
});
var _jsxFileName = "/Users/brians/git/react-slider/src/components/ReactSlider/ReactSlider.jsx";
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
import React from 'react';
import PropTypes from 'prop-types';
/**
* To prevent text selection while dragging.
* http://stackoverflow.com/questions/5429827/how-can-i-prevent-text-element-selection-with-cursor-drag
*/
function pauseEvent(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
if (e.preventDefault) {
e.preventDefault();
}
return false;
}
function stopPropagation(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
}
function ensureArray(x) {
if (x == null) {
return [];
}
return Array.isArray(x) ? x : [x];
}
function undoEnsureArray(x) {
return x !== null && x.length === 1 ? x[0] : x;
}
function trimSucceeding(length, nextValue, minDistance, max) {
for (var i = 0; i < length; i += 1) {
var padding = max - i * minDistance;
if (nextValue[length - 1 - i] > padding) {
// eslint-disable-next-line no-param-reassign
nextValue[length - 1 - i] = padding;
}
}
}
function trimPreceding(length, nextValue, minDistance, min) {
for (var i = 0; i < length; i += 1) {
var padding = min + i * minDistance;
if (nextValue[i] < padding) {
// eslint-disable-next-line no-param-reassign
nextValue[i] = padding;
}
}
}
function addHandlers(eventMap) {
Object.keys(eventMap).forEach(function (key) {
if (typeof document !== 'undefined') {
document.addEventListener(key, eventMap[key], false);
}
});
}
function removeHandlers(eventMap) {
Object.keys(eventMap).forEach(function (key) {
if (typeof document !== 'undefined') {
document.removeEventListener(key, eventMap[key], false);
}
});
}
var ReactSlider =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(ReactSlider, _React$Component);
function ReactSlider(_props) {
var _this;
_this = _React$Component.call(this, _props) || this;
_this.onMouseUp = function () {
_this.onEnd(_this.getMouseEventMap());
};
_this.onTouchEnd = function () {
_this.onEnd(_this.getTouchEventMap());
};
_this.onBlur = function () {
_this.setState({
index: -1
}, _this.onEnd(_this.getKeyDownEventMap()));
};
_this.onMouseMove = function (e) {
var position = _this.getMousePosition(e);
var diffPosition = _this.getDiffPosition(position[0]);
var newValue = _this.getValueFromPosition(diffPosition);
_this.move(newValue);
};
_this.onTouchMove = function (e) {
if (e.touches.length > 1) {
return;
}
var position = _this.getTouchPosition(e);
if (typeof _this.isScrolling === 'undefined') {
var diffMainDir = position[0] - _this.startPosition[0];
var diffScrollDir = position[1] - _this.startPosition[1];
_this.isScrolling = Math.abs(diffScrollDir) > Math.abs(diffMainDir);
}
if (_this.isScrolling) {
_this.setState({
index: -1
});
return;
}
pauseEvent(e);
var diffPosition = _this.getDiffPosition(position[0]);
var newValue = _this.getValueFromPosition(diffPosition);
_this.move(newValue);
};
_this.onKeyDown = function (e) {
if (e.ctrlKey || e.shiftKey || e.altKey) {
return;
}
switch (e.key) {
case 'ArrowLeft':
case 'ArrowDown':
case 'Left':
case 'Down':
e.preventDefault();
_this.moveDownByStep();
break;
case 'ArrowRight':
case 'ArrowUp':
case 'Right':
case 'Up':
e.preventDefault();
_this.moveUpByStep();
break;
case 'Home':
e.preventDefault();
_this.move(_this.props.min);
break;
case 'End':
e.preventDefault();
_this.move(_this.props.max);
break;
case 'PageDown':
e.preventDefault();
_this.moveDownByStep(_this.props.pageFn(_this.props.step));
break;
case 'PageUp':
e.preventDefault();
_this.moveUpByStep(_this.props.pageFn(_this.props.step));
break;
default:
}
};
_this.onSliderMouseDown = function (e) {
// do nothing if disabled or right click
if (_this.props.disabled || e.button === 2) {
return;
}
_this.hasMoved = false;
if (!_this.props.snapDragDisabled) {
var position = _this.getMousePosition(e);
_this.forceValueFromPosition(position[0], function (i) {
_this.start(i, position[0]);
_this.fireChangeEvent('onChange');
addHandlers(_this.getMouseEventMap());
});
}
pauseEvent(e);
};
_this.onSliderClick = function (e) {
if (_this.props.disabled) {
return;
}
if (_this.props.onSliderClick && !_this.hasMoved) {
var position = _this.getMousePosition(e);
var valueAtPos = _this.trimAlignValue(_this.calcValue(_this.calcOffsetFromPosition(position[0])));
_this.props.onSliderClick(valueAtPos);
}
};
_this.createOnKeyDown = function (i) {
return function (e) {
if (_this.props.disabled) {
return;
}
_this.start(i);
addHandlers(_this.getKeyDownEventMap());
pauseEvent(e);
};
};
_this.createOnMouseDown = function (i) {
return function (e) {
// do nothing if disabled or right click
if (_this.props.disabled || e.button === 2) {
return;
}
var position = _this.getMousePosition(e);
_this.start(i, position[0]);
addHandlers(_this.getMouseEventMap());
pauseEvent(e);
};
};
_this.createOnTouchStart = function (i) {
return function (e) {
if (_this.props.disabled || e.touches.length > 1) {
return;
}
var position = _this.getTouchPosition(e);
_this.startPosition = position; // don't know yet if the user is trying to scroll
_this.isScrolling = undefined;
_this.start(i, position[0]);
addHandlers(_this.getTouchEventMap());
stopPropagation(e);
};
};
_this.handleResize = function () {
// setTimeout of 0 gives element enough time to have assumed its new size if
// it is being resized
var resizeTimeout = window.setTimeout(function () {
// drop this timeout from pendingResizeTimeouts to reduce memory usage
_this.pendingResizeTimeouts.shift();
_this.resize();
}, 0);
_this.pendingResizeTimeouts.push(resizeTimeout);
};
_this.renderThumb = function (style, i) {
var className = _this.props.thumbClassName + " " + _this.props.thumbClassName + "-" + i + " " + (_this.state.index === i ? _this.props.thumbActiveClassName : '');
var props = {
ref: function ref(r) {
_this["thumb" + i] = r;
},
key: _this.props.thumbClassName + "-" + i,
className: className,
style: style,
onMouseDown: _this.createOnMouseDown(i),
onTouchStart: _this.createOnTouchStart(i),
onFocus: _this.createOnKeyDown(i),
tabIndex: 0,
role: 'slider',
'aria-orientation': _this.props.orientation,
'aria-valuenow': _this.state.value[i],
'aria-valuemin': _this.props.min,
'aria-valuemax': _this.props.max,
'aria-label': Array.isArray(_this.props.ariaLabel) ? _this.props.ariaLabel[i] : _this.props.ariaLabel
};
var state = {
index: i,
value: undoEnsureArray(_this.state.value),
valueNow: _this.state.value[i]
};
if (_this.props.ariaValuetext) {
props['aria-valuetext'] = typeof _this.props.ariaValuetext === 'string' ? _this.props.ariaValuetext : _this.props.ariaValuetext(state);
}
return _this.props.renderThumb(props, state);
};
_this.renderTrack = function (i, offsetFrom, offsetTo) {
var props = {
key: _this.props.trackClassName + "-" + i,
className: _this.props.trackClassName + " " + _this.props.trackClassName + "-" + i,
style: _this.buildTrackStyle(offsetFrom, _this.state.upperBound - offsetTo)
};
var state = {
index: i,
value: undoEnsureArray(_this.state.value)
};
return _this.props.renderTrack(props, state);
};
var value = ensureArray(_props.value);
if (!value.length) {
value = ensureArray(_props.defaultValue);
} // reused throughout the component to store results of iterations over `value`
_this.tempArray = value.slice(); // array for storing resize timeouts ids
_this.pendingResizeTimeouts = [];
var zIndices = [];
for (var i = 0; i < value.length; i += 1) {
value[i] = _this.trimAlignValue(value[i], _props);
zIndices.push(i);
}
_this.state = {
index: -1,
upperBound: 0,
sliderLength: 0,
value: value,
zIndices: zIndices
};
return _this;
}
var _proto = ReactSlider.prototype;
_proto.componentDidMount = function componentDidMount() {
if (typeof window !== 'undefined') {
window.addEventListener('resize', this.handleResize);
this.resize();
}
} // Keep the internal `value` consistent with an outside `value` if present.
// This basically allows the slider to be a controlled component.
;
_proto.componentWillReceiveProps = function componentWillReceiveProps(newProps) {
var value = ensureArray(newProps.value);
if (!value.length) {
// eslint-disable-next-line prefer-destructuring
value = this.state.value;
} // ensure the array keeps the same size as `value`
this.tempArray = value.slice();
for (var i = 0; i < value.length; i += 1) {
this.state.value[i] = this.trimAlignValue(value[i], newProps);
}
if (this.state.value.length > value.length) {
this.state.value.length = value.length;
} // If an upperBound has not yet been determined (due to the component being hidden
// during the mount event, or during the last resize), then calculate it now
if (this.state.upperBound === 0) {
this.resize();
}
};
_proto.componentWillUnmount = function componentWillUnmount() {
this.clearPendingResizeTimeouts();
if (typeof window !== 'undefined') {
window.removeEventListener('resize', this.handleResize);
}
};
_proto.onEnd = function onEnd(eventMap) {
removeHandlers(eventMap);
this.fireChangeEvent('onAfterChange');
};
_proto.getValue = function getValue() {
return undoEnsureArray(this.state.value);
};
_proto.getClosestIndex = function getClosestIndex(pixelOffset) {
var minDist = Number.MAX_VALUE;
var closestIndex = -1;
var value = this.state.value;
var l = value.length;
for (var i = 0; i < l; i += 1) {
var offset = this.calcOffset(value[i]);
var dist = Math.abs(pixelOffset - offset);
if (dist < minDist) {
minDist = dist;
closestIndex = i;
}
}
return closestIndex;
};
_proto.getMousePosition = function getMousePosition(e) {
return [e["page" + this.axisKey()], e["page" + this.orthogonalAxisKey()]];
};
_proto.getTouchPosition = function getTouchPosition(e) {
var touch = e.touches[0];
return [touch["page" + this.axisKey()], touch["page" + this.orthogonalAxisKey()]];
};
_proto.getKeyDownEventMap = function getKeyDownEventMap() {
return {
keydown: this.onKeyDown,
focusout: this.onBlur
};
};
_proto.getMouseEventMap = function getMouseEventMap() {
return {
mousemove: this.onMouseMove,
mouseup: this.onMouseUp
};
};
_proto.getTouchEventMap = function getTouchEventMap() {
return {
touchmove: this.onTouchMove,
touchend: this.onTouchEnd
};
};
_proto.getValueFromPosition = function getValueFromPosition(position) {
var diffValue = position / (this.state.sliderLength - this.state.thumbSize) * (this.props.max - this.props.min);
return this.trimAlignValue(this.state.startValue + diffValue);
};
_proto.getDiffPosition = function getDiffPosition(position) {
var diffPosition = position - this.state.startPosition;
if (this.props.invert) {
diffPosition *= -1;
}
return diffPosition;
} // create the `keydown` handler for the i-th thumb
;
_proto.resize = function resize() {
var slider = this.slider;
var thumb = this.thumb0;
var rect = slider.getBoundingClientRect();
var sizeKey = this.sizeKey();
var sliderMax = rect[this.posMaxKey()];
var sliderMin = rect[this.posMinKey()];
this.setState({
upperBound: slider[sizeKey] - thumb[sizeKey],
sliderLength: Math.abs(sliderMax - sliderMin),
thumbSize: thumb[sizeKey],
sliderStart: this.props.invert ? sliderMax : sliderMin
});
} // calculates the offset of a thumb in pixels based on its value.
;
_proto.calcOffset = function calcOffset(value) {
var range = this.props.max - this.props.min;
if (range === 0) {
return 0;
}
var ratio = (value - this.props.min) / range;
return ratio * this.state.upperBound;
} // calculates the value corresponding to a given pixel offset, i.e. the inverse of `calcOffset`.
;
_proto.calcValue = function calcValue(offset) {
var ratio = offset / this.state.upperBound;
return ratio * (this.props.max - this.props.min) + this.props.min;
};
_proto.calcOffsetFromPosition = function calcOffsetFromPosition(position) {
var pixelOffset = position - this.state.sliderStart;
if (this.props.invert) {
pixelOffset = this.state.sliderLength - pixelOffset;
}
pixelOffset -= this.state.thumbSize / 2;
return pixelOffset;
} // Snaps the nearest thumb to the value corresponding to `position`
// and calls `callback` with that thumb's index.
;
_proto.forceValueFromPosition = function forceValueFromPosition(position, callback) {
var pixelOffset = this.calcOffsetFromPosition(position);
var closestIndex = this.getClosestIndex(pixelOffset);
var nextValue = this.trimAlignValue(this.calcValue(pixelOffset)); // Clone this.state.value since we'll modify it temporarily
// eslint-disable-next-line zillow/react/no-access-state-in-setstate
var value = this.state.value.slice();
value[closestIndex] = nextValue; // Prevents the slider from shrinking below `props.minDistance`
for (var i = 0; i < value.length - 1; i += 1) {
if (value[i + 1] - value[i] < this.props.minDistance) {
return;
}
}
this.setState({
value: value
}, callback.bind(this, closestIndex));
} // clear all pending timeouts to avoid error messages after unmounting
;
_proto.clearPendingResizeTimeouts = function clearPendingResizeTimeouts() {
do {
var nextTimeout = this.pendingResizeTimeouts.shift();
clearTimeout(nextTimeout);
} while (this.pendingResizeTimeouts.length);
};
_proto.start = function start(i, position) {
var thumbRef = this["thumb" + i];
thumbRef.focus();
this.hasMoved = false;
this.fireChangeEvent('onBeforeChange');
var zIndices = this.state.zIndices; // remove wherever the element is
zIndices.splice(zIndices.indexOf(i), 1); // add to end
zIndices.push(i);
this.setState(function (prevState) {
return {
startValue: prevState.value[i],
startPosition: position !== undefined ? position : prevState.startPosition,
index: i,
zIndices: zIndices
};
});
};
_proto.moveUpByStep = function moveUpByStep(step) {
if (step === void 0) {
step = this.props.step;
}
var oldValue = this.state.value[this.state.index];
var newValue = oldValue + step;
this.move(Math.min(newValue, this.props.max));
};
_proto.moveDownByStep = function moveDownByStep(step) {
if (step === void 0) {
step = this.props.step;
}
var oldValue = this.state.value[this.state.index];
var newValue = oldValue - step;
this.move(Math.max(newValue, this.props.min));
};
_proto.move = function move(newValue) {
this.hasMoved = true;
var _this$state = this.state,
index = _this$state.index,
value = _this$state.value;
var length = value.length;
var oldValue = value[index];
var _this$props = this.props,
pearling = _this$props.pearling,
max = _this$props.max,
min = _this$props.min,
minDistance = _this$props.minDistance; // if "pearling" (= thumbs pushing each other) is disabled,
// prevent the thumb from getting closer than `minDistance` to the previous or next thumb.
if (!pearling) {
if (index > 0) {
var valueBefore = value[index - 1];
if (newValue < valueBefore + minDistance) {
// eslint-disable-next-line no-param-reassign
newValue = valueBefore + minDistance;
}
}
if (index < length - 1) {
var valueAfter = value[index + 1];
if (newValue > valueAfter - minDistance) {
// eslint-disable-next-line no-param-reassign
newValue = valueAfter - minDistance;
}
}
}
value[index] = newValue; // if "pearling" is enabled, let the current thumb push the pre- and succeeding thumbs.
if (pearling && length > 1) {
if (newValue > oldValue) {
this.pushSucceeding(value, minDistance, index);
trimSucceeding(length, value, minDistance, max);
} else if (newValue < oldValue) {
this.pushPreceding(value, minDistance, index);
trimPreceding(length, value, minDistance, min);
}
} // Normally you would use `shouldComponentUpdate`,
// but since the slider is a low-level component,
// the extra complexity might be worth the extra performance.
if (newValue !== oldValue) {
this.setState({
value: value
}, this.fireChangeEvent.bind(this, 'onChange'));
}
};
_proto.pushSucceeding = function pushSucceeding(value, minDistance, index) {
var i;
var padding;
for (i = index, padding = value[i] + minDistance; value[i + 1] !== null && padding > value[i + 1]; i += 1, padding = value[i] + minDistance) {
// eslint-disable-next-line no-param-reassign
value[i + 1] = this.alignValue(padding);
}
};
_proto.pushPreceding = function pushPreceding(value, minDistance, index) {
for (var i = index, padding = value[i] - minDistance; value[i - 1] !== null && padding < value[i - 1]; i -= 1, padding = value[i] - minDistance) {
// eslint-disable-next-line no-param-reassign
value[i - 1] = this.alignValue(padding);
}
};
_proto.axisKey = function axisKey() {
if (this.props.orientation === 'vertical') {
return 'Y';
} // Defaults to 'horizontal';
return 'X';
};
_proto.orthogonalAxisKey = function orthogonalAxisKey() {
if (this.props.orientation === 'vertical') {
return 'X';
} // Defaults to 'horizontal'
return 'Y';
};
_proto.posMinKey = function posMinKey() {
if (this.props.orientation === 'vertical') {
return this.props.invert ? 'bottom' : 'top';
} // Defaults to 'horizontal'
return this.props.invert ? 'right' : 'left';
};
_proto.posMaxKey = function posMaxKey() {
if (this.props.orientation === 'vertical') {
return this.props.invert ? 'top' : 'bottom';
} // Defaults to 'horizontal'
return this.props.invert ? 'left' : 'right';
};
_proto.sizeKey = function sizeKey() {
if (this.props.orientation === 'vertical') {
return 'clientHeight';
} // Defaults to 'horizontal'
return 'clientWidth';
};
_proto.trimAlignValue = function trimAlignValue(val, props) {
return this.alignValue(this.trimValue(val, props), props);
};
_proto.trimValue = function trimValue(val, props) {
if (props === void 0) {
props = this.props;
}
var trimmed = val;
if (trimmed <= props.min) {
trimmed = props.min;
}
if (trimmed >= props.max) {
trimmed = props.max;
}
return trimmed;
};
_proto.alignValue = function alignValue(val, props) {
if (props === void 0) {
props = this.props;
}
var valModStep = (val - props.min) % props.step;
var alignValue = val - valModStep;
if (Math.abs(valModStep) * 2 >= props.step) {
alignValue += valModStep > 0 ? props.step : -props.step;
}
return parseFloat(alignValue.toFixed(5));
};
_proto.fireChangeEvent = function fireChangeEvent(event) {
if (this.props[event]) {
this.props[event](undoEnsureArray(this.state.value));
}
};
_proto.buildThumbStyle = function buildThumbStyle(offset, i) {
var style = {
position: 'absolute',
willChange: this.state.index >= 0 ? this.posMinKey() : '',
zIndex: this.state.zIndices.indexOf(i) + 1
};
style[this.posMinKey()] = offset + "px";
return style;
};
_proto.buildTrackStyle = function buildTrackStyle(min, max) {
var obj = {
position: 'absolute',
willChange: this.state.index >= 0 ? this.posMinKey() + "," + this.posMaxKey() : ''
};
obj[this.posMinKey()] = min;
obj[this.posMaxKey()] = max;
return obj;
};
_proto.renderThumbs = function renderThumbs(offset) {
var length = offset.length;
var styles = this.tempArray;
for (var i = 0; i < length; i += 1) {
styles[i] = this.buildThumbStyle(offset[i], i);
}
var res = [];
for (var _i = 0; _i < length; _i += 1) {
res[_i] = this.renderThumb(styles[_i], _i);
}
return res;
};
_proto.renderTracks = function renderTracks(offset) {
var tracks = [];
var lastIndex = offset.length - 1;
tracks.push(this.renderTrack(0, 0, offset[0]));
for (var i = 0; i < lastIndex; i += 1) {
tracks.push(this.renderTrack(i + 1, offset[i], offset[i + 1]));
}
tracks.push(this.renderTrack(lastIndex + 1, offset[lastIndex], this.state.upperBound));
return tracks;
};
_proto.render = function render() {
var _this2 = this;
var offset = this.tempArray;
var value = this.state.value;
var l = value.length;
for (var i = 0; i < l; i += 1) {
offset[i] = this.calcOffset(value[i], i);
}
var tracks = this.props.withTracks ? this.renderTracks(offset) : null;
var thumbs = this.renderThumbs(offset);
return React.createElement('div', {
ref: function ref(r) {
_this2.slider = r;
},
style: {
position: 'relative'
},
className: this.props.className + (this.props.disabled ? ' disabled' : ''),
onMouseDown: this.onSliderMouseDown,
onClick: this.onSliderClick
}, tracks, thumbs);
};
return ReactSlider;
}(React.Component);
ReactSlider.displayName = 'ReactSlider';
ReactSlider.defaultProps = {
min: 0,
max: 100,
step: 1,
pageFn: function pageFn(step) {
return step * 10;
},
minDistance: 0,
defaultValue: 0,
orientation: 'horizontal',
className: 'slider',
thumbClassName: 'thumb',
thumbActiveClassName: 'active',
trackClassName: 'track',
withTracks: true,
pearling: false,
disabled: false,
snapDragDisabled: false,
invert: false,
renderThumb: function renderThumb(props) {
return React.createElement("div", _extends({}, props, {
__source: {
fileName: _jsxFileName,
lineNumber: 279
},
__self: this
}));
},
renderTrack: function renderTrack(props) {
return React.createElement("div", _extends({}, props, {
__source: {
fileName: _jsxFileName,
lineNumber: 280
},
__self: this
}));
}
};
ReactSlider.propTypes = process.env.NODE_ENV !== "production" ? {
/**
* The minimum value of the slider.
*/
min: PropTypes.number,
/**
* The maximum value of the slider.
*/
max: PropTypes.number,
/**
* Value to be added or subtracted on each step the slider makes.
* Must be greater than zero.
* `max - min` should be evenly divisible by the step value.
*/
step: PropTypes.number,
/**
* The result of the function is the value to be added or subtracted
* when the `Page Up` or `Page Down` keys are pressed.
*
* The current `step` value will be passed as the only argument.
* By default, paging will modify `step` by a factor of 10.
*/
pageFn: PropTypes.func,
/**
* The minimal distance between any pair of thumbs.
* Must be positive, but zero means they can sit on top of each other.
*/
minDistance: PropTypes.number,
/**
* Determines the initial positions of the thumbs and the number of thumbs.
*
* If a number is passed a slider with one thumb will be rendered.
* If an array is passed each value will determine the position of one thumb.
* The values in the array must be sorted.
*/
defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.arrayOf(PropTypes.number)]),
/**
* Like `defaultValue` but for
* [controlled components](http://facebook.github.io/react/docs/forms.html#controlled-components).
*/
// eslint-disable-next-line zillow/react/require-default-props
value: PropTypes.oneOfType([PropTypes.number, PropTypes.arrayOf(PropTypes.number)]),
/**
* Determines whether the slider moves horizontally (from left to right)
* or vertically (from top to bottom).
*/
orientation: PropTypes.oneOf(['horizontal', 'vertical']),
/**
* The css class set on the slider node.
*/
className: PropTypes.string,
/**
* The css class set on each thumb node.
*
* In addition each thumb will receive a numbered css class of the form
* `${thumbClassName}-${i}`, e.g. `thumb-0`, `thumb-1`, ...
*/
thumbClassName: PropTypes.string,
/**
* The css class set on the thumb that is currently being moved.
*/
thumbActiveClassName: PropTypes.string,
/**
* If `true` tracks between the thumbs will be rendered.
*/
withTracks: PropTypes.bool,
/**
* The css class set on the tracks between the thumbs.
* In addition track fragment will receive a numbered css class of the form
* `${trackClassName}-${i}`, e.g. `track-0`, `track-1`, ...
*/
trackClassName: PropTypes.string,
/**
* If `true` the active thumb will push other thumbs
* within the constraints of `min`, `max`, `step` and `minDistance`.
*/
pearling: PropTypes.bool,
/**
* If `true` the thumbs can't be moved.
*/
disabled: PropTypes.bool,
/**
* Disables thumb move when clicking the slider track
*/
snapDragDisabled: PropTypes.bool,
/**
* Inverts the slider.
*/
invert: PropTypes.bool,
/**
* Callback called before starting to move a thumb.
*/
// eslint-disable-next-line max-len
// eslint-disable-next-line zillow/react/require-default-props, zillow/react/no-unused-prop-types
onBeforeChange: PropTypes.func,
/**
* Callback called on every value change.
*/
// eslint-disable-next-line max-len
// eslint-disable-next-line zillow/react/require-default-props, zillow/react/no-unused-prop-types
onChange: PropTypes.func,
/**
* Callback called only after moving a thumb has ended.
*/
// eslint-disable-next-line max-len
// eslint-disable-next-line zillow/react/require-default-props, zillow/react/no-unused-prop-types
onAfterChange: PropTypes.func,
/**
* Callback called when the the slider is clicked (thumb or tracks).
* Receives the value at the clicked position as argument.
*/
// eslint-disable-next-line zillow/react/require-default-props
onSliderClick: PropTypes.func,
/**
* aria-label for screen-readers to apply to the thumbs.
* Use an array for more than one thumb.
* The length of the array must match the number of thumbs in the value array.
*/
// eslint-disable-next-line zillow/react/require-default-props
ariaLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
/**
* aria-valuetext for screen-readers.
* Can be a static string, or a function that returns a string.
* The function will be passed a single argument,
* an object with the following properties:
*
* state => `Value: ${state.value}`
*
* - `state.index` {`number`} the index of the thumb
* - `state.value` {`number` | `array`} the current value state
* - `state.valueNow` {`number`} the value of the thumb (i.e. aria-valuenow)
*/
// eslint-disable-next-line zillow/react/require-default-props
ariaValuetext: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
/**
* Provide a custom render function for the track node.
* The render function will be passed two arguments,
* an object with props that should be added to your handle node,
* and an object with track and slider state:
*
* (props, state) => <div {...props} />
*
* - `props` {`object`} props to be spread into your track node
* - `state.index` {`number`} the index of the track
* - `state.value` {`number` | `array`} the current value state
*/
renderTrack: PropTypes.func,
/**
* Provide a custom render function for dynamic thumb content.
* The render function will be passed two arguments,
* an object with props that should be added to your thumb node,
* and an object with thumb and slider state:
*
* (props, state) => <div {...props} />
*
* - `props` {`object`} props to be spread into your thumb node
* - `state.index` {`number`} the index of the thumb
* - `state.value` {`number` | `array`} the current value state
* - `state.valueNow` {`number`} the value of the thumb (i.e. aria-valuenow)
*/
// eslint-disable-next-line zillow/react/require-default-props
renderThumb: PropTypes.func
} : {};
export default ReactSlider;
Single slider, similar to `<input type="range" defaultValue={0} />`
```jsx
<ReactSlider
className="horizontal-slider"
thumbClassName="example-thumb"
trackClassName="example-track"
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
/>
```
Double slider
```jsx
<ReactSlider
className="horizontal-slider"
thumbClassName="example-thumb"
trackClassName="example-track"
defaultValue={[0, 100]}
ariaLabel={['Lower thumb', 'Upper thumb']}
ariaValuetext={state => `Thumb value ${state.valueNow}`}
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
pearling
minDistance={10}
/>
```
Multi slider
```jsx
<ReactSlider
className="horizontal-slider"
thumbClassName="example-thumb"
trackClassName="example-track"
defaultValue={[0, 50, 100]}
ariaLabel={['Leftmost thumb', 'Middle thumb', 'Rightmost thumb']}
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
pearling
minDistance={10}
/>
```
Vertical slider
```jsx
<ReactSlider
className="vertical-slider"
thumbClassName="example-thumb"
trackClassName="example-track"
defaultValue={[0, 50, 100]}
ariaLabel={['Lowest thumb', 'Middle thumb', 'Top thumb']}
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
orientation="vertical"
invert
pearling
minDistance={10}
/>
```
Custom styling using [styled-components](https://www.styled-components.com/)
```jsx
import styled from 'styled-components';
const StyledSlider = styled(ReactSlider)`
width: 100%;
height: 25px;
`;
const StyledThumb = styled.div`
height: 25px;
line-height: 25px;
width: 25px;
text-align: center;
background-color: #000;
color: #fff;
border-radius: 50%;
cursor: grab;
`;
const Thumb = (props, state) => <StyledThumb {...props}>{state.valueNow}</StyledThumb>;
const StyledTrack = styled.div`
top: 0;
bottom: 0;
background: ${props => props.index === 2 ? '#f00' : props.index === 1 ? '#0f0' : '#ddd'};
border-radius: 999px;
`;
const Track = (props, state) => <StyledTrack {...props} index={state.index} />;
<StyledSlider
defaultValue={[50, 75]}
renderTrack={Track}
renderThumb={Thumb}
/>
```
var _jsxFileName = "/Users/brians/git/react-slider/src/styleguidist/ThemeWrapper.jsx";
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _templateObject() {
var data = _taggedTemplateLiteralLoose(["\n .horizontal-slider {\n width: 100%;\n max-width: 500px;\n height: 50px;\n border: 1px solid grey;\n }\n\n .vertical-slider {\n height: 380px;\n width: 50px;\n border: 1px solid grey;\n }\n\n .example-thumb {\n font-size: 0.9em;\n text-align: center;\n background-color: black;\n color: white;\n cursor: pointer;\n }\n\n .example-thumb.active {\n background-color: grey;\n }\n\n .example-track {\n position: relative;\n background: #ddd;\n }\n\n .example-track.example-track-1 {\n background: #f00;\n }\n\n .example-track.example-track-2 {\n background: #0f0;\n }\n\n .horizontal-slider .example-track {\n top: 20px;\n height: 10px;\n }\n\n .horizontal-slider .example-thumb {\n top: 1px;\n width: 50px;\n height: 48px;\n line-height: 48px;\n }\n\n .vertical-slider .example-thumb {\n left: 1px;\n width: 48px;\n line-height: 50px;\n height: 50px;\n }\n\n .vertical-slider .example-track {\n left: 20px;\n width: 10px;\n }\n"]);
_templateObject = function _templateObject() {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
import React from 'react'; // eslint-disable-next-line zillow/import/no-extraneous-dependencies
import { createGlobalStyle } from 'styled-components';
var GlobalStyle = createGlobalStyle(_templateObject());
export default (function (props) {
return React.createElement(React.Fragment, {
__source: {
fileName: _jsxFileName,
lineNumber: 70
},
__self: this
}, React.createElement(GlobalStyle, {
__source: {
fileName: _jsxFileName,
lineNumber: 71
},
__self: this
}), React.createElement("div", _extends({}, props, {
__source: {
fileName: _jsxFileName,
lineNumber: 72
},
__self: this
})));
});

Sorry, the diff of this file is not supported yet

"use strict";
var _react = _interopRequireDefault(require("react"));
var _reactTestRenderer = _interopRequireDefault(require("react-test-renderer"));
var _ReactSlider = _interopRequireDefault(require("../ReactSlider"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
describe('<ReactSlider>', function () {
it('can render', function () {
var tree = _reactTestRenderer.default.create(_react.default.createElement(_ReactSlider.default, null)).toJSON();
expect(tree).toMatchSnapshot();
});
it('should call onAfterChange callback when onEnd is called', function () {
var onAfterChange = jest.fn();
var testRenderer = _reactTestRenderer.default.create(_react.default.createElement(_ReactSlider.default, {
onAfterChange: onAfterChange
}));
var testInstance = testRenderer.root;
expect(onAfterChange).not.toHaveBeenCalled();
testInstance.instance.onBlur();
expect(onAfterChange).toHaveBeenCalledTimes(1);
});
});
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
/**
* To prevent text selection while dragging.
* http://stackoverflow.com/questions/5429827/how-can-i-prevent-text-element-selection-with-cursor-drag
*/
function pauseEvent(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
if (e.preventDefault) {
e.preventDefault();
}
return false;
}
function stopPropagation(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
}
function ensureArray(x) {
if (x == null) {
return [];
}
return Array.isArray(x) ? x : [x];
}
function undoEnsureArray(x) {
return x !== null && x.length === 1 ? x[0] : x;
}
function trimSucceeding(length, nextValue, minDistance, max) {
for (var i = 0; i < length; i += 1) {
var padding = max - i * minDistance;
if (nextValue[length - 1 - i] > padding) {
// eslint-disable-next-line no-param-reassign
nextValue[length - 1 - i] = padding;
}
}
}
function trimPreceding(length, nextValue, minDistance, min) {
for (var i = 0; i < length; i += 1) {
var padding = min + i * minDistance;
if (nextValue[i] < padding) {
// eslint-disable-next-line no-param-reassign
nextValue[i] = padding;
}
}
}
function addHandlers(eventMap) {
Object.keys(eventMap).forEach(function (key) {
if (typeof document !== 'undefined') {
document.addEventListener(key, eventMap[key], false);
}
});
}
function removeHandlers(eventMap) {
Object.keys(eventMap).forEach(function (key) {
if (typeof document !== 'undefined') {
document.removeEventListener(key, eventMap[key], false);
}
});
}
var ReactSlider =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(ReactSlider, _React$Component);
function ReactSlider(_props) {
var _this;
_this = _React$Component.call(this, _props) || this;
_this.onMouseUp = function () {
_this.onEnd(_this.getMouseEventMap());
};
_this.onTouchEnd = function () {
_this.onEnd(_this.getTouchEventMap());
};
_this.onBlur = function () {
_this.setState({
index: -1
}, _this.onEnd(_this.getKeyDownEventMap()));
};
_this.onMouseMove = function (e) {
var position = _this.getMousePosition(e);
var diffPosition = _this.getDiffPosition(position[0]);
var newValue = _this.getValueFromPosition(diffPosition);
_this.move(newValue);
};
_this.onTouchMove = function (e) {
if (e.touches.length > 1) {
return;
}
var position = _this.getTouchPosition(e);
if (typeof _this.isScrolling === 'undefined') {
var diffMainDir = position[0] - _this.startPosition[0];
var diffScrollDir = position[1] - _this.startPosition[1];
_this.isScrolling = Math.abs(diffScrollDir) > Math.abs(diffMainDir);
}
if (_this.isScrolling) {
_this.setState({
index: -1
});
return;
}
pauseEvent(e);
var diffPosition = _this.getDiffPosition(position[0]);
var newValue = _this.getValueFromPosition(diffPosition);
_this.move(newValue);
};
_this.onKeyDown = function (e) {
if (e.ctrlKey || e.shiftKey || e.altKey) {
return;
}
switch (e.key) {
case 'ArrowLeft':
case 'ArrowDown':
case 'Left':
case 'Down':
e.preventDefault();
_this.moveDownByStep();
break;
case 'ArrowRight':
case 'ArrowUp':
case 'Right':
case 'Up':
e.preventDefault();
_this.moveUpByStep();
break;
case 'Home':
e.preventDefault();
_this.move(_this.props.min);
break;
case 'End':
e.preventDefault();
_this.move(_this.props.max);
break;
case 'PageDown':
e.preventDefault();
_this.moveDownByStep(_this.props.pageFn(_this.props.step));
break;
case 'PageUp':
e.preventDefault();
_this.moveUpByStep(_this.props.pageFn(_this.props.step));
break;
default:
}
};
_this.onSliderMouseDown = function (e) {
// do nothing if disabled or right click
if (_this.props.disabled || e.button === 2) {
return;
}
_this.hasMoved = false;
if (!_this.props.snapDragDisabled) {
var position = _this.getMousePosition(e);
_this.forceValueFromPosition(position[0], function (i) {
_this.start(i, position[0]);
_this.fireChangeEvent('onChange');
addHandlers(_this.getMouseEventMap());
});
}
pauseEvent(e);
};
_this.onSliderClick = function (e) {
if (_this.props.disabled) {
return;
}
if (_this.props.onSliderClick && !_this.hasMoved) {
var position = _this.getMousePosition(e);
var valueAtPos = _this.trimAlignValue(_this.calcValue(_this.calcOffsetFromPosition(position[0])));
_this.props.onSliderClick(valueAtPos);
}
};
_this.createOnKeyDown = function (i) {
return function (e) {
if (_this.props.disabled) {
return;
}
_this.start(i);
addHandlers(_this.getKeyDownEventMap());
pauseEvent(e);
};
};
_this.createOnMouseDown = function (i) {
return function (e) {
// do nothing if disabled or right click
if (_this.props.disabled || e.button === 2) {
return;
}
var position = _this.getMousePosition(e);
_this.start(i, position[0]);
addHandlers(_this.getMouseEventMap());
pauseEvent(e);
};
};
_this.createOnTouchStart = function (i) {
return function (e) {
if (_this.props.disabled || e.touches.length > 1) {
return;
}
var position = _this.getTouchPosition(e);
_this.startPosition = position; // don't know yet if the user is trying to scroll
_this.isScrolling = undefined;
_this.start(i, position[0]);
addHandlers(_this.getTouchEventMap());
stopPropagation(e);
};
};
_this.handleResize = function () {
// setTimeout of 0 gives element enough time to have assumed its new size if
// it is being resized
var resizeTimeout = window.setTimeout(function () {
// drop this timeout from pendingResizeTimeouts to reduce memory usage
_this.pendingResizeTimeouts.shift();
_this.resize();
}, 0);
_this.pendingResizeTimeouts.push(resizeTimeout);
};
_this.renderThumb = function (style, i) {
var className = _this.props.thumbClassName + " " + _this.props.thumbClassName + "-" + i + " " + (_this.state.index === i ? _this.props.thumbActiveClassName : '');
var props = {
ref: function ref(r) {
_this["thumb" + i] = r;
},
key: _this.props.thumbClassName + "-" + i,
className: className,
style: style,
onMouseDown: _this.createOnMouseDown(i),
onTouchStart: _this.createOnTouchStart(i),
onFocus: _this.createOnKeyDown(i),
tabIndex: 0,
role: 'slider',
'aria-orientation': _this.props.orientation,
'aria-valuenow': _this.state.value[i],
'aria-valuemin': _this.props.min,
'aria-valuemax': _this.props.max,
'aria-label': Array.isArray(_this.props.ariaLabel) ? _this.props.ariaLabel[i] : _this.props.ariaLabel
};
var state = {
index: i,
value: undoEnsureArray(_this.state.value),
valueNow: _this.state.value[i]
};
if (_this.props.ariaValuetext) {
props['aria-valuetext'] = typeof _this.props.ariaValuetext === 'string' ? _this.props.ariaValuetext : _this.props.ariaValuetext(state);
}
return _this.props.renderThumb(props, state);
};
_this.renderTrack = function (i, offsetFrom, offsetTo) {
var props = {
key: _this.props.trackClassName + "-" + i,
className: _this.props.trackClassName + " " + _this.props.trackClassName + "-" + i,
style: _this.buildTrackStyle(offsetFrom, _this.state.upperBound - offsetTo)
};
var state = {
index: i,
value: undoEnsureArray(_this.state.value)
};
return _this.props.renderTrack(props, state);
};
var value = ensureArray(_props.value);
if (!value.length) {
value = ensureArray(_props.defaultValue);
} // reused throughout the component to store results of iterations over `value`
_this.tempArray = value.slice(); // array for storing resize timeouts ids
_this.pendingResizeTimeouts = [];
var zIndices = [];
for (var i = 0; i < value.length; i += 1) {
value[i] = _this.trimAlignValue(value[i], _props);
zIndices.push(i);
}
_this.state = {
index: -1,
upperBound: 0,
sliderLength: 0,
value: value,
zIndices: zIndices
};
return _this;
}
var _proto = ReactSlider.prototype;
_proto.componentDidMount = function componentDidMount() {
if (typeof window !== 'undefined') {
window.addEventListener('resize', this.handleResize);
this.resize();
}
} // Keep the internal `value` consistent with an outside `value` if present.
// This basically allows the slider to be a controlled component.
;
_proto.componentWillReceiveProps = function componentWillReceiveProps(newProps) {
var value = ensureArray(newProps.value);
if (!value.length) {
// eslint-disable-next-line prefer-destructuring
value = this.state.value;
} // ensure the array keeps the same size as `value`
this.tempArray = value.slice();
for (var i = 0; i < value.length; i += 1) {
this.state.value[i] = this.trimAlignValue(value[i], newProps);
}
if (this.state.value.length > value.length) {
this.state.value.length = value.length;
} // If an upperBound has not yet been determined (due to the component being hidden
// during the mount event, or during the last resize), then calculate it now
if (this.state.upperBound === 0) {
this.resize();
}
};
_proto.componentWillUnmount = function componentWillUnmount() {
this.clearPendingResizeTimeouts();
if (typeof window !== 'undefined') {
window.removeEventListener('resize', this.handleResize);
}
};
_proto.onEnd = function onEnd(eventMap) {
removeHandlers(eventMap);
this.fireChangeEvent('onAfterChange');
};
_proto.getValue = function getValue() {
return undoEnsureArray(this.state.value);
};
_proto.getClosestIndex = function getClosestIndex(pixelOffset) {
var minDist = Number.MAX_VALUE;
var closestIndex = -1;
var value = this.state.value;
var l = value.length;
for (var i = 0; i < l; i += 1) {
var offset = this.calcOffset(value[i]);
var dist = Math.abs(pixelOffset - offset);
if (dist < minDist) {
minDist = dist;
closestIndex = i;
}
}
return closestIndex;
};
_proto.getMousePosition = function getMousePosition(e) {
return [e["page" + this.axisKey()], e["page" + this.orthogonalAxisKey()]];
};
_proto.getTouchPosition = function getTouchPosition(e) {
var touch = e.touches[0];
return [touch["page" + this.axisKey()], touch["page" + this.orthogonalAxisKey()]];
};
_proto.getKeyDownEventMap = function getKeyDownEventMap() {
return {
keydown: this.onKeyDown,
focusout: this.onBlur
};
};
_proto.getMouseEventMap = function getMouseEventMap() {
return {
mousemove: this.onMouseMove,
mouseup: this.onMouseUp
};
};
_proto.getTouchEventMap = function getTouchEventMap() {
return {
touchmove: this.onTouchMove,
touchend: this.onTouchEnd
};
};
_proto.getValueFromPosition = function getValueFromPosition(position) {
var diffValue = position / (this.state.sliderLength - this.state.thumbSize) * (this.props.max - this.props.min);
return this.trimAlignValue(this.state.startValue + diffValue);
};
_proto.getDiffPosition = function getDiffPosition(position) {
var diffPosition = position - this.state.startPosition;
if (this.props.invert) {
diffPosition *= -1;
}
return diffPosition;
} // create the `keydown` handler for the i-th thumb
;
_proto.resize = function resize() {
var slider = this.slider;
var thumb = this.thumb0;
var rect = slider.getBoundingClientRect();
var sizeKey = this.sizeKey();
var sliderMax = rect[this.posMaxKey()];
var sliderMin = rect[this.posMinKey()];
this.setState({
upperBound: slider[sizeKey] - thumb[sizeKey],
sliderLength: Math.abs(sliderMax - sliderMin),
thumbSize: thumb[sizeKey],
sliderStart: this.props.invert ? sliderMax : sliderMin
});
} // calculates the offset of a thumb in pixels based on its value.
;
_proto.calcOffset = function calcOffset(value) {
var range = this.props.max - this.props.min;
if (range === 0) {
return 0;
}
var ratio = (value - this.props.min) / range;
return ratio * this.state.upperBound;
} // calculates the value corresponding to a given pixel offset, i.e. the inverse of `calcOffset`.
;
_proto.calcValue = function calcValue(offset) {
var ratio = offset / this.state.upperBound;
return ratio * (this.props.max - this.props.min) + this.props.min;
};
_proto.calcOffsetFromPosition = function calcOffsetFromPosition(position) {
var pixelOffset = position - this.state.sliderStart;
if (this.props.invert) {
pixelOffset = this.state.sliderLength - pixelOffset;
}
pixelOffset -= this.state.thumbSize / 2;
return pixelOffset;
} // Snaps the nearest thumb to the value corresponding to `position`
// and calls `callback` with that thumb's index.
;
_proto.forceValueFromPosition = function forceValueFromPosition(position, callback) {
var pixelOffset = this.calcOffsetFromPosition(position);
var closestIndex = this.getClosestIndex(pixelOffset);
var nextValue = this.trimAlignValue(this.calcValue(pixelOffset)); // Clone this.state.value since we'll modify it temporarily
// eslint-disable-next-line zillow/react/no-access-state-in-setstate
var value = this.state.value.slice();
value[closestIndex] = nextValue; // Prevents the slider from shrinking below `props.minDistance`
for (var i = 0; i < value.length - 1; i += 1) {
if (value[i + 1] - value[i] < this.props.minDistance) {
return;
}
}
this.setState({
value: value
}, callback.bind(this, closestIndex));
} // clear all pending timeouts to avoid error messages after unmounting
;
_proto.clearPendingResizeTimeouts = function clearPendingResizeTimeouts() {
do {
var nextTimeout = this.pendingResizeTimeouts.shift();
clearTimeout(nextTimeout);
} while (this.pendingResizeTimeouts.length);
};
_proto.start = function start(i, position) {
var thumbRef = this["thumb" + i];
thumbRef.focus();
this.hasMoved = false;
this.fireChangeEvent('onBeforeChange');
var zIndices = this.state.zIndices; // remove wherever the element is
zIndices.splice(zIndices.indexOf(i), 1); // add to end
zIndices.push(i);
this.setState(function (prevState) {
return {
startValue: prevState.value[i],
startPosition: position !== undefined ? position : prevState.startPosition,
index: i,
zIndices: zIndices
};
});
};
_proto.moveUpByStep = function moveUpByStep(step) {
if (step === void 0) {
step = this.props.step;
}
var oldValue = this.state.value[this.state.index];
var newValue = oldValue + step;
this.move(Math.min(newValue, this.props.max));
};
_proto.moveDownByStep = function moveDownByStep(step) {
if (step === void 0) {
step = this.props.step;
}
var oldValue = this.state.value[this.state.index];
var newValue = oldValue - step;
this.move(Math.max(newValue, this.props.min));
};
_proto.move = function move(newValue) {
this.hasMoved = true;
var _this$state = this.state,
index = _this$state.index,
value = _this$state.value;
var length = value.length;
var oldValue = value[index];
var _this$props = this.props,
pearling = _this$props.pearling,
max = _this$props.max,
min = _this$props.min,
minDistance = _this$props.minDistance; // if "pearling" (= thumbs pushing each other) is disabled,
// prevent the thumb from getting closer than `minDistance` to the previous or next thumb.
if (!pearling) {
if (index > 0) {
var valueBefore = value[index - 1];
if (newValue < valueBefore + minDistance) {
// eslint-disable-next-line no-param-reassign
newValue = valueBefore + minDistance;
}
}
if (index < length - 1) {
var valueAfter = value[index + 1];
if (newValue > valueAfter - minDistance) {
// eslint-disable-next-line no-param-reassign
newValue = valueAfter - minDistance;
}
}
}
value[index] = newValue; // if "pearling" is enabled, let the current thumb push the pre- and succeeding thumbs.
if (pearling && length > 1) {
if (newValue > oldValue) {
this.pushSucceeding(value, minDistance, index);
trimSucceeding(length, value, minDistance, max);
} else if (newValue < oldValue) {
this.pushPreceding(value, minDistance, index);
trimPreceding(length, value, minDistance, min);
}
} // Normally you would use `shouldComponentUpdate`,
// but since the slider is a low-level component,
// the extra complexity might be worth the extra performance.
if (newValue !== oldValue) {
this.setState({
value: value
}, this.fireChangeEvent.bind(this, 'onChange'));
}
};
_proto.pushSucceeding = function pushSucceeding(value, minDistance, index) {
var i;
var padding;
for (i = index, padding = value[i] + minDistance; value[i + 1] !== null && padding > value[i + 1]; i += 1, padding = value[i] + minDistance) {
// eslint-disable-next-line no-param-reassign
value[i + 1] = this.alignValue(padding);
}
};
_proto.pushPreceding = function pushPreceding(value, minDistance, index) {
for (var i = index, padding = value[i] - minDistance; value[i - 1] !== null && padding < value[i - 1]; i -= 1, padding = value[i] - minDistance) {
// eslint-disable-next-line no-param-reassign
value[i - 1] = this.alignValue(padding);
}
};
_proto.axisKey = function axisKey() {
if (this.props.orientation === 'vertical') {
return 'Y';
} // Defaults to 'horizontal';
return 'X';
};
_proto.orthogonalAxisKey = function orthogonalAxisKey() {
if (this.props.orientation === 'vertical') {
return 'X';
} // Defaults to 'horizontal'
return 'Y';
};
_proto.posMinKey = function posMinKey() {
if (this.props.orientation === 'vertical') {
return this.props.invert ? 'bottom' : 'top';
} // Defaults to 'horizontal'
return this.props.invert ? 'right' : 'left';
};
_proto.posMaxKey = function posMaxKey() {
if (this.props.orientation === 'vertical') {
return this.props.invert ? 'top' : 'bottom';
} // Defaults to 'horizontal'
return this.props.invert ? 'left' : 'right';
};
_proto.sizeKey = function sizeKey() {
if (this.props.orientation === 'vertical') {
return 'clientHeight';
} // Defaults to 'horizontal'
return 'clientWidth';
};
_proto.trimAlignValue = function trimAlignValue(val, props) {
return this.alignValue(this.trimValue(val, props), props);
};
_proto.trimValue = function trimValue(val, props) {
if (props === void 0) {
props = this.props;
}
var trimmed = val;
if (trimmed <= props.min) {
trimmed = props.min;
}
if (trimmed >= props.max) {
trimmed = props.max;
}
return trimmed;
};
_proto.alignValue = function alignValue(val, props) {
if (props === void 0) {
props = this.props;
}
var valModStep = (val - props.min) % props.step;
var alignValue = val - valModStep;
if (Math.abs(valModStep) * 2 >= props.step) {
alignValue += valModStep > 0 ? props.step : -props.step;
}
return parseFloat(alignValue.toFixed(5));
};
_proto.fireChangeEvent = function fireChangeEvent(event) {
if (this.props[event]) {
this.props[event](undoEnsureArray(this.state.value));
}
};
_proto.buildThumbStyle = function buildThumbStyle(offset, i) {
var style = {
position: 'absolute',
willChange: this.state.index >= 0 ? this.posMinKey() : '',
zIndex: this.state.zIndices.indexOf(i) + 1
};
style[this.posMinKey()] = offset + "px";
return style;
};
_proto.buildTrackStyle = function buildTrackStyle(min, max) {
var obj = {
position: 'absolute',
willChange: this.state.index >= 0 ? this.posMinKey() + "," + this.posMaxKey() : ''
};
obj[this.posMinKey()] = min;
obj[this.posMaxKey()] = max;
return obj;
};
_proto.renderThumbs = function renderThumbs(offset) {
var length = offset.length;
var styles = this.tempArray;
for (var i = 0; i < length; i += 1) {
styles[i] = this.buildThumbStyle(offset[i], i);
}
var res = [];
for (var _i = 0; _i < length; _i += 1) {
res[_i] = this.renderThumb(styles[_i], _i);
}
return res;
};
_proto.renderTracks = function renderTracks(offset) {
var tracks = [];
var lastIndex = offset.length - 1;
tracks.push(this.renderTrack(0, 0, offset[0]));
for (var i = 0; i < lastIndex; i += 1) {
tracks.push(this.renderTrack(i + 1, offset[i], offset[i + 1]));
}
tracks.push(this.renderTrack(lastIndex + 1, offset[lastIndex], this.state.upperBound));
return tracks;
};
_proto.render = function render() {
var _this2 = this;
var offset = this.tempArray;
var value = this.state.value;
var l = value.length;
for (var i = 0; i < l; i += 1) {
offset[i] = this.calcOffset(value[i], i);
}
var tracks = this.props.withTracks ? this.renderTracks(offset) : null;
var thumbs = this.renderThumbs(offset);
return _react.default.createElement('div', {
ref: function ref(r) {
_this2.slider = r;
},
style: {
position: 'relative'
},
className: this.props.className + (this.props.disabled ? ' disabled' : ''),
onMouseDown: this.onSliderMouseDown,
onClick: this.onSliderClick
}, tracks, thumbs);
};
return ReactSlider;
}(_react.default.Component);
ReactSlider.displayName = 'ReactSlider';
ReactSlider.defaultProps = {
min: 0,
max: 100,
step: 1,
pageFn: function pageFn(step) {
return step * 10;
},
minDistance: 0,
defaultValue: 0,
orientation: 'horizontal',
className: 'slider',
thumbClassName: 'thumb',
thumbActiveClassName: 'active',
trackClassName: 'track',
withTracks: true,
pearling: false,
disabled: false,
snapDragDisabled: false,
invert: false,
renderThumb: function renderThumb(props) {
return _react.default.createElement("div", props);
},
renderTrack: function renderTrack(props) {
return _react.default.createElement("div", props);
}
};
ReactSlider.propTypes = process.env.NODE_ENV !== "production" ? {
/**
* The minimum value of the slider.
*/
min: _propTypes.default.number,
/**
* The maximum value of the slider.
*/
max: _propTypes.default.number,
/**
* Value to be added or subtracted on each step the slider makes.
* Must be greater than zero.
* `max - min` should be evenly divisible by the step value.
*/
step: _propTypes.default.number,
/**
* The result of the function is the value to be added or subtracted
* when the `Page Up` or `Page Down` keys are pressed.
*
* The current `step` value will be passed as the only argument.
* By default, paging will modify `step` by a factor of 10.
*/
pageFn: _propTypes.default.func,
/**
* The minimal distance between any pair of thumbs.
* Must be positive, but zero means they can sit on top of each other.
*/
minDistance: _propTypes.default.number,
/**
* Determines the initial positions of the thumbs and the number of thumbs.
*
* If a number is passed a slider with one thumb will be rendered.
* If an array is passed each value will determine the position of one thumb.
* The values in the array must be sorted.
*/
defaultValue: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.arrayOf(_propTypes.default.number)]),
/**
* Like `defaultValue` but for
* [controlled components](http://facebook.github.io/react/docs/forms.html#controlled-components).
*/
// eslint-disable-next-line zillow/react/require-default-props
value: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.arrayOf(_propTypes.default.number)]),
/**
* Determines whether the slider moves horizontally (from left to right)
* or vertically (from top to bottom).
*/
orientation: _propTypes.default.oneOf(['horizontal', 'vertical']),
/**
* The css class set on the slider node.
*/
className: _propTypes.default.string,
/**
* The css class set on each thumb node.
*
* In addition each thumb will receive a numbered css class of the form
* `${thumbClassName}-${i}`, e.g. `thumb-0`, `thumb-1`, ...
*/
thumbClassName: _propTypes.default.string,
/**
* The css class set on the thumb that is currently being moved.
*/
thumbActiveClassName: _propTypes.default.string,
/**
* If `true` tracks between the thumbs will be rendered.
*/
withTracks: _propTypes.default.bool,
/**
* The css class set on the tracks between the thumbs.
* In addition track fragment will receive a numbered css class of the form
* `${trackClassName}-${i}`, e.g. `track-0`, `track-1`, ...
*/
trackClassName: _propTypes.default.string,
/**
* If `true` the active thumb will push other thumbs
* within the constraints of `min`, `max`, `step` and `minDistance`.
*/
pearling: _propTypes.default.bool,
/**
* If `true` the thumbs can't be moved.
*/
disabled: _propTypes.default.bool,
/**
* Disables thumb move when clicking the slider track
*/
snapDragDisabled: _propTypes.default.bool,
/**
* Inverts the slider.
*/
invert: _propTypes.default.bool,
/**
* Callback called before starting to move a thumb.
*/
// eslint-disable-next-line max-len
// eslint-disable-next-line zillow/react/require-default-props, zillow/react/no-unused-prop-types
onBeforeChange: _propTypes.default.func,
/**
* Callback called on every value change.
*/
// eslint-disable-next-line max-len
// eslint-disable-next-line zillow/react/require-default-props, zillow/react/no-unused-prop-types
onChange: _propTypes.default.func,
/**
* Callback called only after moving a thumb has ended.
*/
// eslint-disable-next-line max-len
// eslint-disable-next-line zillow/react/require-default-props, zillow/react/no-unused-prop-types
onAfterChange: _propTypes.default.func,
/**
* Callback called when the the slider is clicked (thumb or tracks).
* Receives the value at the clicked position as argument.
*/
// eslint-disable-next-line zillow/react/require-default-props
onSliderClick: _propTypes.default.func,
/**
* aria-label for screen-readers to apply to the thumbs.
* Use an array for more than one thumb.
* The length of the array must match the number of thumbs in the value array.
*/
// eslint-disable-next-line zillow/react/require-default-props
ariaLabel: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.arrayOf(_propTypes.default.string)]),
/**
* aria-valuetext for screen-readers.
* Can be a static string, or a function that returns a string.
* The function will be passed a single argument,
* an object with the following properties:
*
* state => `Value: ${state.value}`
*
* - `state.index` {`number`} the index of the thumb
* - `state.value` {`number` | `array`} the current value state
* - `state.valueNow` {`number`} the value of the thumb (i.e. aria-valuenow)
*/
// eslint-disable-next-line zillow/react/require-default-props
ariaValuetext: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.func]),
/**
* Provide a custom render function for the track node.
* The render function will be passed two arguments,
* an object with props that should be added to your handle node,
* and an object with track and slider state:
*
* (props, state) => <div {...props} />
*
* - `props` {`object`} props to be spread into your track node
* - `state.index` {`number`} the index of the track
* - `state.value` {`number` | `array`} the current value state
*/
renderTrack: _propTypes.default.func,
/**
* Provide a custom render function for dynamic thumb content.
* The render function will be passed two arguments,
* an object with props that should be added to your thumb node,
* and an object with thumb and slider state:
*
* (props, state) => <div {...props} />
*
* - `props` {`object`} props to be spread into your thumb node
* - `state.index` {`number`} the index of the thumb
* - `state.value` {`number` | `array`} the current value state
* - `state.valueNow` {`number`} the value of the thumb (i.e. aria-valuenow)
*/
// eslint-disable-next-line zillow/react/require-default-props
renderThumb: _propTypes.default.func
} : {};
var _default = ReactSlider;
exports.default = _default;
Single slider, similar to `<input type="range" defaultValue={0} />`
```jsx
<ReactSlider
className="horizontal-slider"
thumbClassName="example-thumb"
trackClassName="example-track"
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
/>
```
Double slider
```jsx
<ReactSlider
className="horizontal-slider"
thumbClassName="example-thumb"
trackClassName="example-track"
defaultValue={[0, 100]}
ariaLabel={['Lower thumb', 'Upper thumb']}
ariaValuetext={state => `Thumb value ${state.valueNow}`}
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
pearling
minDistance={10}
/>
```
Multi slider
```jsx
<ReactSlider
className="horizontal-slider"
thumbClassName="example-thumb"
trackClassName="example-track"
defaultValue={[0, 50, 100]}
ariaLabel={['Leftmost thumb', 'Middle thumb', 'Rightmost thumb']}
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
pearling
minDistance={10}
/>
```
Vertical slider
```jsx
<ReactSlider
className="vertical-slider"
thumbClassName="example-thumb"
trackClassName="example-track"
defaultValue={[0, 50, 100]}
ariaLabel={['Lowest thumb', 'Middle thumb', 'Top thumb']}
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
orientation="vertical"
invert
pearling
minDistance={10}
/>
```
Custom styling using [styled-components](https://www.styled-components.com/)
```jsx
import styled from 'styled-components';
const StyledSlider = styled(ReactSlider)`
width: 100%;
height: 25px;
`;
const StyledThumb = styled.div`
height: 25px;
line-height: 25px;
width: 25px;
text-align: center;
background-color: #000;
color: #fff;
border-radius: 50%;
cursor: grab;
`;
const Thumb = (props, state) => <StyledThumb {...props}>{state.valueNow}</StyledThumb>;
const StyledTrack = styled.div`
top: 0;
bottom: 0;
background: ${props => props.index === 2 ? '#f00' : props.index === 1 ? '#0f0' : '#ddd'};
border-radius: 999px;
`;
const Track = (props, state) => <StyledTrack {...props} index={state.index} />;
<StyledSlider
defaultValue={[50, 75]}
renderTrack={Track}
renderThumb={Thumb}
/>
```
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _styledComponents = require("styled-components");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _templateObject() {
var data = _taggedTemplateLiteralLoose(["\n .horizontal-slider {\n width: 100%;\n max-width: 500px;\n height: 50px;\n border: 1px solid grey;\n }\n\n .vertical-slider {\n height: 380px;\n width: 50px;\n border: 1px solid grey;\n }\n\n .example-thumb {\n font-size: 0.9em;\n text-align: center;\n background-color: black;\n color: white;\n cursor: pointer;\n }\n\n .example-thumb.active {\n background-color: grey;\n }\n\n .example-track {\n position: relative;\n background: #ddd;\n }\n\n .example-track.example-track-1 {\n background: #f00;\n }\n\n .example-track.example-track-2 {\n background: #0f0;\n }\n\n .horizontal-slider .example-track {\n top: 20px;\n height: 10px;\n }\n\n .horizontal-slider .example-thumb {\n top: 1px;\n width: 50px;\n height: 48px;\n line-height: 48px;\n }\n\n .vertical-slider .example-thumb {\n left: 1px;\n width: 48px;\n line-height: 50px;\n height: 50px;\n }\n\n .vertical-slider .example-track {\n left: 20px;\n width: 10px;\n }\n"]);
_templateObject = function _templateObject() {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
var GlobalStyle = (0, _styledComponents.createGlobalStyle)(_templateObject());
var _default = function _default(props) {
return _react.default.createElement(_react.default.Fragment, null, _react.default.createElement(GlobalStyle, null), _react.default.createElement("div", props));
};
exports.default = _default;
+57
-11
{
"name": "react-slider",
"version": "0.11.2",
"version": "1.0.0-0",
"description": "Slider component for React",
"main": "react-slider.js",
"main": "lib/components/ReactSlider/ReactSlider.js",
"module": "es/components/ReactSlider/ReactSlider.js",
"sideEffects": false,
"files": [
"es",
"lib"
],
"keywords": [

@@ -13,2 +19,45 @@ "react-component",

"author": "Michał Powaga <michalpowaga13@gmail.com>",
"scripts": {
"build": "create-react-styleguide script build",
"build:watch": "create-react-styleguide script build:watch",
"build:styleguide": "create-react-styleguide script build:styleguide",
"clean": "create-react-styleguide script clean",
"eslint": "create-react-styleguide script eslint",
"eslint:fix": "create-react-styleguide script eslint:fix",
"start": "create-react-styleguide script start",
"test": "create-react-styleguide script test",
"test:coverage": "create-react-styleguide script test:coverage",
"test:update": "create-react-styleguide script test:update",
"test:watch": "create-react-styleguide script test:watch",
"prepublishOnly": "create-react-styleguide script prepublishOnly",
"predeploy": "npm run build:styleguide",
"deploy": "gh-pages -d styleguide",
"release": "standard-version"
},
"husky": {
"hooks": {
"pre-commit": "npm run eslint && npm run test"
}
},
"dependencies": {},
"peerDependencies": {
"react": "16.x",
"prop-types": "^15.6"
},
"devDependencies": {
"babel-plugin-styled-components": "^1.10.0",
"babel-preset-zillow": "^4.0.3",
"create-react-styleguide": "^4.0.3",
"eslint-plugin-jest": "^22.4.1",
"eslint-plugin-zillow": "^3.1.0",
"gh-pages": "^2.0.1",
"husky": "^1.3.1",
"jest-styled-components": "^6.3.1",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-test-renderer": "^16.8.6",
"standard-version": "^5.0.2",
"styled-components": "^4.2.0"
},
"contributors": [

@@ -22,17 +71,14 @@ {

"email": "james.22au@gmail.com"
},
{
"name": "Brian Stone",
"email": "brians@zillowgroup.com"
}
],
"homepage": "https://github.com/zillow/react-slider",
"license": "MIT",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/mpowaga/react-slider.git"
},
"peerDependencies": {
"react": "^16.2",
"prop-types": "^15.6",
"create-react-class": "^15.6"
"url": "https://github.com/zillow/react-slider.git"
}
}
+5
-147
# React Slider
[![npm version](https://badge.fury.io/js/react-slider.svg)](https://badge.fury.io/js/react-slider)
CSS agnostic slider component for React.
See demo: [https://mpowaga.github.io/react-slider/](https://mpowaga.github.io/react-slider/)
### Installation

@@ -13,152 +13,10 @@

### Overview
### API Documentation
#### Single slider:
For component props, methods, and living examples, see the demo:
Similar to `<input type="range" defaultValue={50} />`
https://zillow.github.io/react-slider/
```javascript
React.render(<ReactSlider defaultValue={50} />, document.body);
```
#### Double slider (with bars between the handles):
```javascript
React.render(<ReactSlider defaultValue={[0, 100]} withBars />, document.body);
```
#### Multi slider:
```javascript
React.render(<ReactSlider defaultValue={[0, 33, 67, 100]} withBars />, document.body);
```
#### Provide custom handles:
```javascript
React.render(
<ReactSlider withBars>
<div className="my-handle">1</div>
<div className="my-handle">2</div>
<div className="my-handle">3</div>
</ReactSlider>,
document.body
);
```
Now you can style it as you want. Checkout the `index.html` example to see how.
### Properties
##### min {number} default: 0
The minimum value of the slider.
##### max {number} default: 100
The maximum value of the slider.
##### step {number} default: 1
Value to be added or subtracted on each step the slider makes.
Must be greater than zero.
`max - min` should be evenly divisible by the step value.
##### minDistance {number} default: 0
The minimal distance between any pair of handles.
Zero means they can sit on top of each other.
##### defaultValue {oneOfType([number, arrayOf(number)])} default: 0
Determines the initial positions of the handles and the number of handles if the component has no children.
If a number is passed a slider with one handle will be rendered.
If an array is passed each value will determine the position of one handle.
The values in the array must be sorted.
If the component has children, the length of the array must match the number of children.
##### value {oneOfType([number, arrayOf(number)])} default: 0
Like `defaultValue` but for [controlled components](http://facebook.github.io/react/docs/forms.html#controlled-components).
##### orientation {oneOf(['horizontal', 'vertical'])} default: 'horizontal'
Determines whether the slider moves horizontally (from left to right) or vertically (from top to bottom).
##### className {string} default: 'slider'
The css class set on the slider node.
##### handleClassName {string} default: 'handle'
The css class set on each handle node.
In addition each handle will receive a numbered css class of the form `${handleClassName}-${i}`,
e.g. `handle-0`, `handle-1`, ...
##### handleActiveClassName {string} default: 'active'
The css class set on the handle that is currently being moved.
##### withBars {boolean} default: false
If `true` bars between the handles will be rendered.
##### barClassName {string} default: 'bar'
The css class set on the bars between the handles.
In addition bar fragment will receive a numbered css class of the form `${barClassName}-${i}`,
e.g. `bar-0`, `bar-1`, ...
##### pearling {bool} default: false
If `true` the active handle will push other handles within the constraints of `min`, `max`, `step` and `minDistance`.
##### disabled {bool} default: false
If `true` the handles can't be moved.
##### snapDragDisabled {bool} default: false
Disables handle move when clicking the slider bar.
##### invert {bool} default: false
Inverts the slider.
##### onBeforeChange {func}
Callback called before starting to move a handle.
##### onChange {func}
Callback called on every value change.
##### onAfterChange {func}
Callback called only after moving a handle has ended or when a new value is set by clicking on the slider.
##### onSliderClick {func}
Callback called when the the slider is clicked (handle or bars). Receives the value at the clicked position as argument.
##### ariaLabel {oneOfType([string, arrayOf(string)])}
aria-label for screen-readers to apply to the handles.
Use an array for more than one handle.
The length of the array must match the number of handles in the value array.
##### ariaValuetext {string}
aria-valuetext for screen-readers
### Methods
##### getValue
Returns the current value of the slider, which is a number in the case of a single slider,
or an array of numbers in case of a multi slider.
### License
See the [License](LICENSE) file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>React Slider Example</title>
<script src="node_modules/react/umd/react.production.min.js"></script>
<script src="node_modules/react-dom/umd/react-dom.production.min.js"></script>
<script src="node_modules/create-react-class/create-react-class.js"></script>
<script src="node_modules/prop-types/prop-types.js"></script>
<script src="react-slider.js"></script>
<style>
body {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.horizontal-slider {
width: 100%;
max-width: 500px;
height: 50px;
border: 1px solid grey;
}
.vertical-slider {
height: 380px;
width: 50px;
border: 1px solid grey;
}
.handle {
font-size: 0.9em;
text-align: center;
background-color: black;
color: white;
cursor: pointer;
}
.handle.active {
background-color: grey;
}
.bar {
position: relative;
background: #ddd;
}
.bar.bar-1 {
background: #f00;
}
.bar.bar-2 {
background: #0f0;
}
.horizontal-slider .bar {
top: 20px;
height: 10px;
}
.horizontal-slider .handle {
top: 1px;
width: 50px;
height: 48px;
line-height: 48px;
}
.vertical-slider .handle {
left: 1px;
width: 48px;
line-height: 50px;
}
.vertical-slider .bar {
left: 20px;
width: 10px;
}
#horizontal-0,
#horizontal-1, #horizontal-2, #horizontal-3,
#vertical {
margin: 20px 10px;
}
#horizontal-2 .bar.bar-2 {
background: #ddd;
}
</style>
</head>
<body>
<div id="horizontal-0"></div>
<div id="horizontal-1"></div>
<div id="horizontal-2"></div>
<div id="horizontal-3"></div>
<div id="vertical"></div>
<script>
var ReactSlider = React.createFactory(ReactSlider);
function map(v, f, context) {
return (v && v.map) ? v.map(f, context) : f.call(context, v, 0);
}
var Demo = React.createFactory(createReactClass({
displayName: 'Demo',
getInitialState: function () {
return {value: this.props.defaultValue}
},
onChange: function (value) {
this.setState({value: value});
},
render: function () {
return ReactSlider(
Object.assign({}, this.props, {
className: this.props.orientation + '-slider',
pearling: true,
minDistance: 10,
value: this.state.value,
onChange: this.onChange
}, this.props),
map(this.state.value, function (value, i) {
return React.createElement('div', {key: i}, value);
})
);
}
}));
ReactDOM.render(Demo({
defaultValue: 0,
orientation: 'horizontal',
ariaLabel: 'This is a single handle',
}), document.getElementById('horizontal-1'));
ReactDOM.render(Demo({
defaultValue: [0, 100],
orientation: 'horizontal',
withBars: true,
ariaLabel: ['Lower handle', 'Upper handle'],
ariaValuetext: 'Some arbitrary value',
}), document.getElementById('horizontal-2'));
ReactDOM.render(Demo({
defaultValue: [0, 50, 100],
orientation: 'horizontal',
withBars: true,
ariaLabel: ['Leftmost handle', 'Middle handle', 'Rightmost handle'],
}), document.getElementById('horizontal-3'));
ReactDOM.render(Demo({
defaultValue: [0, 50, 100],
orientation: 'vertical',
withBars: true,
invert: true,
ariaLabel: ['Lowest handle', 'Middle handle', 'Top handle'],
}), document.getElementById('vertical'));
</script>
</body>
</html>
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['react','prop-types','create-react-class'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('react'),require('prop-types'),require('create-react-class'));
} else {
root.ReactSlider = factory(root.React,root.PropTypes,root.createReactClass);
}
}(this, function (React, PropTypes, createReactClass) {
/**
* To prevent text selection while dragging.
* http://stackoverflow.com/questions/5429827/how-can-i-prevent-text-element-selection-with-cursor-drag
*/
function pauseEvent(e) {
if (e.stopPropagation) e.stopPropagation();
if (e.preventDefault) e.preventDefault();
return false;
}
function stopPropagation(e) {
if (e.stopPropagation) e.stopPropagation();
}
/**
* Spreads `count` values equally between `min` and `max`.
*/
function linspace(min, max, count) {
var range = (max - min) / (count - 1);
var res = [];
for (var i = 0; i < count; i++) {
res.push(min + range * i);
}
return res;
}
function ensureArray(x) {
return x == null ? [] : Array.isArray(x) ? x : [x];
}
function undoEnsureArray(x) {
return x != null && x.length === 1 ? x[0] : x;
}
var isArray = Array.isArray || function(x) {
return Object.prototype.toString.call(x) === '[object Array]';
};
// undoEnsureArray(ensureArray(x)) === x
var ReactSlider = createReactClass({
displayName: 'ReactSlider',
propTypes: {
/**
* The minimum value of the slider.
*/
min: PropTypes.number,
/**
* The maximum value of the slider.
*/
max: PropTypes.number,
/**
* Value to be added or subtracted on each step the slider makes.
* Must be greater than zero.
* `max - min` should be evenly divisible by the step value.
*/
step: PropTypes.number,
/**
* The minimal distance between any pair of handles.
* Must be positive, but zero means they can sit on top of each other.
*/
minDistance: PropTypes.number,
/**
* Determines the initial positions of the handles and the number of handles if the component has no children.
*
* If a number is passed a slider with one handle will be rendered.
* If an array is passed each value will determine the position of one handle.
* The values in the array must be sorted.
* If the component has children, the length of the array must match the number of children.
*/
defaultValue: PropTypes.oneOfType([
PropTypes.number,
PropTypes.arrayOf(PropTypes.number)
]),
/**
* Like `defaultValue` but for [controlled components](http://facebook.github.io/react/docs/forms.html#controlled-components).
*/
value: PropTypes.oneOfType([
PropTypes.number,
PropTypes.arrayOf(PropTypes.number)
]),
/**
* Determines whether the slider moves horizontally (from left to right) or vertically (from top to bottom).
*/
orientation: PropTypes.oneOf(['horizontal', 'vertical']),
/**
* The css class set on the slider node.
*/
className: PropTypes.string,
/**
* The css class set on each handle node.
*
* In addition each handle will receive a numbered css class of the form `${handleClassName}-${i}`,
* e.g. `handle-0`, `handle-1`, ...
*/
handleClassName: PropTypes.string,
/**
* The css class set on the handle that is currently being moved.
*/
handleActiveClassName: PropTypes.string,
/**
* If `true` bars between the handles will be rendered.
*/
withBars: PropTypes.bool,
/**
* The css class set on the bars between the handles.
* In addition bar fragment will receive a numbered css class of the form `${barClassName}-${i}`,
* e.g. `bar-0`, `bar-1`, ...
*/
barClassName: PropTypes.string,
/**
* If `true` the active handle will push other handles
* within the constraints of `min`, `max`, `step` and `minDistance`.
*/
pearling: PropTypes.bool,
/**
* If `true` the handles can't be moved.
*/
disabled: PropTypes.bool,
/**
* Disables handle move when clicking the slider bar
*/
snapDragDisabled: PropTypes.bool,
/**
* Inverts the slider.
*/
invert: PropTypes.bool,
/**
* Callback called before starting to move a handle.
*/
onBeforeChange: PropTypes.func,
/**
* Callback called on every value change.
*/
onChange: PropTypes.func,
/**
* Callback called only after moving a handle has ended.
*/
onAfterChange: PropTypes.func,
/**
* Callback called when the the slider is clicked (handle or bars).
* Receives the value at the clicked position as argument.
*/
onSliderClick: PropTypes.func
},
getDefaultProps: function () {
return {
min: 0,
max: 100,
step: 1,
minDistance: 0,
defaultValue: 0,
orientation: 'horizontal',
className: 'slider',
handleClassName: 'handle',
handleActiveClassName: 'active',
barClassName: 'bar',
withBars: false,
pearling: false,
disabled: false,
snapDragDisabled: false,
invert: false
};
},
getInitialState: function () {
var value = this._or(ensureArray(this.props.value), ensureArray(this.props.defaultValue));
// reused throughout the component to store results of iterations over `value`
this.tempArray = value.slice();
// array for storing resize timeouts ids
this.pendingResizeTimeouts = [];
var zIndices = [];
for (var i = 0; i < value.length; i++) {
value[i] = this._trimAlignValue(value[i], this.props);
zIndices.push(i);
}
return {
index: -1,
upperBound: 0,
sliderLength: 0,
value: value,
zIndices: zIndices
};
},
// Keep the internal `value` consistent with an outside `value` if present.
// This basically allows the slider to be a controlled component.
componentWillReceiveProps: function (newProps) {
var value = this._or(ensureArray(newProps.value), this.state.value);
// ensure the array keeps the same size as `value`
this.tempArray = value.slice();
for (var i = 0; i < value.length; i++) {
this.state.value[i] = this._trimAlignValue(value[i], newProps);
}
if (this.state.value.length > value.length)
this.state.value.length = value.length;
// If an upperBound has not yet been determined (due to the component being hidden
// during the mount event, or during the last resize), then calculate it now
if (this.state.upperBound === 0) {
this._resize();
}
},
// Check if the arity of `value` or `defaultValue` matches the number of children (= number of custom handles).
// If no custom handles are provided, just returns `value` if present and `defaultValue` otherwise.
// If custom handles are present but neither `value` nor `defaultValue` are applicable the handles are spread out
// equally.
// TODO: better name? better solution?
_or: function (value, defaultValue) {
var count = React.Children.count(this.props.children);
switch (count) {
case 0:
return value.length > 0 ? value : defaultValue;
case value.length:
return value;
case defaultValue.length:
return defaultValue;
default:
if (value.length !== count || defaultValue.length !== count) {
console.warn(this.constructor.displayName + ": Number of values does not match number of children.");
}
return linspace(this.props.min, this.props.max, count);
}
},
componentDidMount: function () {
window.addEventListener('resize', this._handleResize);
this._resize();
},
componentWillUnmount: function () {
this._clearPendingResizeTimeouts();
window.removeEventListener('resize', this._handleResize);
},
getValue: function () {
return undoEnsureArray(this.state.value);
},
_resize: function () {
var slider = this.slider;
var handle = this.handle0;
var rect = slider.getBoundingClientRect();
var size = this._sizeKey();
var sliderMax = rect[this._posMaxKey()];
var sliderMin = rect[this._posMinKey()];
this.setState({
upperBound: slider[size] - handle[size],
sliderLength: Math.abs(sliderMax - sliderMin),
handleSize: handle[size],
sliderStart: this.props.invert ? sliderMax : sliderMin
});
},
_handleResize: function () {
// setTimeout of 0 gives element enough time to have assumed its new size if it is being resized
var resizeTimeout = window.setTimeout(function() {
// drop this timeout from pendingResizeTimeouts to reduce memory usage
this.pendingResizeTimeouts.shift();
this._resize();
}.bind(this), 0);
this.pendingResizeTimeouts.push(resizeTimeout);
},
// clear all pending timeouts to avoid error messages after unmounting
_clearPendingResizeTimeouts: function() {
do {
var nextTimeout = this.pendingResizeTimeouts.shift();
clearTimeout(nextTimeout);
} while (this.pendingResizeTimeouts.length);
},
// calculates the offset of a handle in pixels based on its value.
_calcOffset: function (value) {
var range = this.props.max - this.props.min;
if (range === 0) {
return 0;
}
var ratio = (value - this.props.min) / range;
return ratio * this.state.upperBound;
},
// calculates the value corresponding to a given pixel offset, i.e. the inverse of `_calcOffset`.
_calcValue: function (offset) {
var ratio = offset / this.state.upperBound;
return ratio * (this.props.max - this.props.min) + this.props.min;
},
_buildHandleStyle: function (offset, i) {
var style = {
position: 'absolute',
willChange: this.state.index >= 0 ? this._posMinKey() : '',
zIndex: this.state.zIndices.indexOf(i) + 1
};
style[this._posMinKey()] = offset + 'px';
return style;
},
_buildBarStyle: function (min, max) {
var obj = {
position: 'absolute',
willChange: this.state.index >= 0 ? this._posMinKey() + ',' + this._posMaxKey() : ''
};
obj[this._posMinKey()] = min;
obj[this._posMaxKey()] = max;
return obj;
},
_getClosestIndex: function (pixelOffset) {
var minDist = Number.MAX_VALUE;
var closestIndex = -1;
var value = this.state.value;
var l = value.length;
for (var i = 0; i < l; i++) {
var offset = this._calcOffset(value[i]);
var dist = Math.abs(pixelOffset - offset);
if (dist < minDist) {
minDist = dist;
closestIndex = i;
}
}
return closestIndex;
},
_calcOffsetFromPosition: function (position) {
var pixelOffset = position - this.state.sliderStart;
if (this.props.invert) pixelOffset = this.state.sliderLength - pixelOffset;
pixelOffset -= (this.state.handleSize / 2);
return pixelOffset;
},
// Snaps the nearest handle to the value corresponding to `position` and calls `callback` with that handle's index.
_forceValueFromPosition: function (position, callback) {
var pixelOffset = this._calcOffsetFromPosition(position);
var closestIndex = this._getClosestIndex(pixelOffset);
var nextValue = this._trimAlignValue(this._calcValue(pixelOffset));
var value = this.state.value.slice(); // Clone this.state.value since we'll modify it temporarily
value[closestIndex] = nextValue;
// Prevents the slider from shrinking below `props.minDistance`
for (var i = 0; i < value.length - 1; i += 1) {
if (value[i + 1] - value[i] < this.props.minDistance) return;
}
this.setState({value: value}, callback.bind(this, closestIndex));
},
_getMousePosition: function (e) {
return [
e['page' + this._axisKey()],
e['page' + this._orthogonalAxisKey()]
];
},
_getTouchPosition: function (e) {
var touch = e.touches[0];
return [
touch['page' + this._axisKey()],
touch['page' + this._orthogonalAxisKey()]
];
},
_getKeyDownEventMap: function () {
return {
'keydown': this._onKeyDown,
'focusout': this._onBlur
}
},
_getMouseEventMap: function () {
return {
'mousemove': this._onMouseMove,
'mouseup': this._onMouseUp
}
},
_getTouchEventMap: function () {
return {
'touchmove': this._onTouchMove,
'touchend': this._onTouchEnd
}
},
// create the `keydown` handler for the i-th handle
_createOnKeyDown: function (i) {
return function (e) {
if (this.props.disabled) return;
this._start(i);
this._addHandlers(this._getKeyDownEventMap());
pauseEvent(e);
}.bind(this);
},
// create the `mousedown` handler for the i-th handle
_createOnMouseDown: function (i) {
return function (e) {
if (this.props.disabled) return;
var position = this._getMousePosition(e);
this._start(i, position[0]);
this._addHandlers(this._getMouseEventMap());
pauseEvent(e);
}.bind(this);
},
// create the `touchstart` handler for the i-th handle
_createOnTouchStart: function (i) {
return function (e) {
if (this.props.disabled || e.touches.length > 1) return;
var position = this._getTouchPosition(e);
this.startPosition = position;
this.isScrolling = undefined; // don't know yet if the user is trying to scroll
this._start(i, position[0]);
this._addHandlers(this._getTouchEventMap());
stopPropagation(e);
}.bind(this);
},
_addHandlers: function (eventMap) {
for (var key in eventMap) {
document.addEventListener(key, eventMap[key], false);
}
},
_removeHandlers: function (eventMap) {
for (var key in eventMap) {
document.removeEventListener(key, eventMap[key], false);
}
},
_start: function (i, position) {
var activeEl = document.activeElement;
var handleRef = this['handle' + i];
// if activeElement is body window will lost focus in IE9
if (activeEl && activeEl != document.body && activeEl != handleRef) {
activeEl.blur && activeEl.blur();
}
this.hasMoved = false;
this._fireChangeEvent('onBeforeChange');
var zIndices = this.state.zIndices;
zIndices.splice(zIndices.indexOf(i), 1); // remove wherever the element is
zIndices.push(i); // add to end
this.setState(function (prevState) {
return {
startValue: this.state.value[i],
startPosition: position !== undefined ? position : prevState.startPosition,
index: i,
zIndices: zIndices
};
});
},
_onMouseUp: function () {
this._onEnd(this._getMouseEventMap());
},
_onTouchEnd: function () {
this._onEnd(this._getTouchEventMap());
},
_onBlur: function () {
this._onEnd(this._getKeyDownEventMap());
},
_onEnd: function (eventMap) {
this._removeHandlers(eventMap);
this.setState({index: -1}, this._fireChangeEvent.bind(this, 'onAfterChange'));
},
_onMouseMove: function (e) {
var position = this._getMousePosition(e);
var diffPosition = this._getDiffPosition(position[0]);
var newValue = this._getValueFromPosition(diffPosition);
this._move(newValue);
},
_onTouchMove: function (e) {
if (e.touches.length > 1) return;
var position = this._getTouchPosition(e);
if (typeof this.isScrolling === 'undefined') {
var diffMainDir = position[0] - this.startPosition[0];
var diffScrollDir = position[1] - this.startPosition[1];
this.isScrolling = Math.abs(diffScrollDir) > Math.abs(diffMainDir);
}
if (this.isScrolling) {
this.setState({index: -1});
return;
}
pauseEvent(e);
var diffPosition = this._getDiffPosition(position[0]);
var newValue = this._getValueFromPosition(diffPosition);
this._move(newValue);
},
_onKeyDown: function (e) {
if (e.ctrlKey || e.shiftKey || e.altKey) return;
switch (e.key) {
case "ArrowLeft":
case "ArrowUp":
e.preventDefault();
return this._moveDownOneStep();
case "ArrowRight":
case "ArrowDown":
e.preventDefault();
return this._moveUpOneStep();
case "Home":
return this._move(this.props.min);
case "End":
return this._move(this.props.max);
default:
return;
}
},
_moveUpOneStep: function () {
var oldValue = this.state.value[this.state.index];
var newValue = oldValue + this.props.step;
this._move(Math.min(newValue, this.props.max));
},
_moveDownOneStep: function () {
var oldValue = this.state.value[this.state.index];
var newValue = oldValue - this.props.step;
this._move(Math.max(newValue, this.props.min));
},
_getValueFromPosition: function (position) {
var diffValue = position / (this.state.sliderLength - this.state.handleSize) * (this.props.max - this.props.min);
return this._trimAlignValue(this.state.startValue + diffValue);
},
_getDiffPosition: function (position) {
var diffPosition = position - this.state.startPosition;
if (this.props.invert) diffPosition *= -1;
return diffPosition;
},
_move: function (newValue) {
this.hasMoved = true;
var props = this.props;
var state = this.state;
var index = state.index;
var value = state.value;
var length = value.length;
var oldValue = value[index];
var minDistance = props.minDistance;
// if "pearling" (= handles pushing each other) is disabled,
// prevent the handle from getting closer than `minDistance` to the previous or next handle.
if (!props.pearling) {
if (index > 0) {
var valueBefore = value[index - 1];
if (newValue < valueBefore + minDistance) {
newValue = valueBefore + minDistance;
}
}
if (index < length - 1) {
var valueAfter = value[index + 1];
if (newValue > valueAfter - minDistance) {
newValue = valueAfter - minDistance;
}
}
}
value[index] = newValue;
// if "pearling" is enabled, let the current handle push the pre- and succeeding handles.
if (props.pearling && length > 1) {
if (newValue > oldValue) {
this._pushSucceeding(value, minDistance, index);
this._trimSucceeding(length, value, minDistance, props.max);
}
else if (newValue < oldValue) {
this._pushPreceding(value, minDistance, index);
this._trimPreceding(length, value, minDistance, props.min);
}
}
// Normally you would use `shouldComponentUpdate`, but since the slider is a low-level component,
// the extra complexity might be worth the extra performance.
if (newValue !== oldValue) {
this.setState({value: value}, this._fireChangeEvent.bind(this, 'onChange'));
}
},
_pushSucceeding: function (value, minDistance, index) {
var i, padding;
for (i = index, padding = value[i] + minDistance;
value[i + 1] != null && padding > value[i + 1];
i++, padding = value[i] + minDistance) {
value[i + 1] = this._alignValue(padding);
}
},
_trimSucceeding: function (length, nextValue, minDistance, max) {
for (var i = 0; i < length; i++) {
var padding = max - i * minDistance;
if (nextValue[length - 1 - i] > padding) {
nextValue[length - 1 - i] = padding;
}
}
},
_pushPreceding: function (value, minDistance, index) {
var i, padding;
for (i = index, padding = value[i] - minDistance;
value[i - 1] != null && padding < value[i - 1];
i--, padding = value[i] - minDistance) {
value[i - 1] = this._alignValue(padding);
}
},
_trimPreceding: function (length, nextValue, minDistance, min) {
for (var i = 0; i < length; i++) {
var padding = min + i * minDistance;
if (nextValue[i] < padding) {
nextValue[i] = padding;
}
}
},
_axisKey: function () {
var orientation = this.props.orientation;
if (orientation === 'horizontal') return 'X';
if (orientation === 'vertical') return 'Y';
},
_orthogonalAxisKey: function () {
var orientation = this.props.orientation;
if (orientation === 'horizontal') return 'Y';
if (orientation === 'vertical') return 'X';
},
_posMinKey: function () {
var orientation = this.props.orientation;
if (orientation === 'horizontal') return this.props.invert ? 'right' : 'left';
if (orientation === 'vertical') return this.props.invert ? 'bottom' : 'top';
},
_posMaxKey: function () {
var orientation = this.props.orientation;
if (orientation === 'horizontal') return this.props.invert ? 'left' : 'right';
if (orientation === 'vertical') return this.props.invert ? 'top' : 'bottom';
},
_sizeKey: function () {
var orientation = this.props.orientation;
if (orientation === 'horizontal') return 'clientWidth';
if (orientation === 'vertical') return 'clientHeight';
},
_trimAlignValue: function (val, props) {
return this._alignValue(this._trimValue(val, props), props);
},
_trimValue: function (val, props) {
props = props || this.props;
if (val <= props.min) val = props.min;
if (val >= props.max) val = props.max;
return val;
},
_alignValue: function (val, props) {
props = props || this.props;
var valModStep = (val - props.min) % props.step;
var alignValue = val - valModStep;
if (Math.abs(valModStep) * 2 >= props.step) {
alignValue += (valModStep > 0) ? props.step : (-props.step);
}
return parseFloat(alignValue.toFixed(5));
},
_renderHandle: function (style, child, i) {
var self = this;
var className = this.props.handleClassName + ' ' +
(this.props.handleClassName + '-' + i) + ' ' +
(this.state.index === i ? this.props.handleActiveClassName : '');
return (
React.createElement('div', {
ref: function (r) {
self['handle' + i] = r;
},
key: 'handle' + i,
className: className,
style: style,
onMouseDown: this._createOnMouseDown(i),
onTouchStart: this._createOnTouchStart(i),
onFocus: this._createOnKeyDown(i),
tabIndex: 0,
role: "slider",
"aria-valuenow": this.state.value[i],
"aria-valuemin": this.props.min,
"aria-valuemax": this.props.max,
"aria-label": isArray(this.props.ariaLabel) ? this.props.ariaLabel[i] : this.props.ariaLabel,
"aria-valuetext": this.props.ariaValuetext,
},
child
)
);
},
_renderHandles: function (offset) {
var length = offset.length;
var styles = this.tempArray;
for (var i = 0; i < length; i++) {
styles[i] = this._buildHandleStyle(offset[i], i);
}
var res = [];
var renderHandle = this._renderHandle;
if (React.Children.count(this.props.children) > 0) {
React.Children.forEach(this.props.children, function (child, i) {
res[i] = renderHandle(styles[i], child, i);
});
} else {
for (i = 0; i < length; i++) {
res[i] = renderHandle(styles[i], null, i);
}
}
return res;
},
_renderBar: function (i, offsetFrom, offsetTo) {
var self = this;
return (
React.createElement('div', {
key: 'bar' + i,
ref: function (r) {
self['bar' + i] = r;
},
className: this.props.barClassName + ' ' + this.props.barClassName + '-' + i,
style: this._buildBarStyle(offsetFrom, this.state.upperBound - offsetTo)
})
);
},
_renderBars: function (offset) {
var bars = [];
var lastIndex = offset.length - 1;
bars.push(this._renderBar(0, 0, offset[0]));
for (var i = 0; i < lastIndex; i++) {
bars.push(this._renderBar(i + 1, offset[i], offset[i + 1]));
}
bars.push(this._renderBar(lastIndex + 1, offset[lastIndex], this.state.upperBound));
return bars;
},
_onSliderMouseDown: function (e) {
if (this.props.disabled) return;
this.hasMoved = false;
if (!this.props.snapDragDisabled) {
var position = this._getMousePosition(e);
this._forceValueFromPosition(position[0], function (i) {
this._start(i, position[0]);
this._fireChangeEvent('onChange');
this._addHandlers(this._getMouseEventMap());
}.bind(this));
}
pauseEvent(e);
},
_onSliderClick: function (e) {
if (this.props.disabled) return;
if (this.props.onSliderClick && !this.hasMoved) {
var position = this._getMousePosition(e);
var valueAtPos = this._trimAlignValue(this._calcValue(this._calcOffsetFromPosition(position[0])));
this.props.onSliderClick(valueAtPos);
}
},
_fireChangeEvent: function (event) {
if (this.props[event]) {
this.props[event](undoEnsureArray(this.state.value));
}
},
render: function () {
var self = this;
var state = this.state;
var props = this.props;
var offset = this.tempArray;
var value = state.value;
var l = value.length;
for (var i = 0; i < l; i++) {
offset[i] = this._calcOffset(value[i], i);
}
var bars = props.withBars ? this._renderBars(offset) : null;
var handles = this._renderHandles(offset);
return (
React.createElement('div', {
ref: function (r) {
self.slider = r;
},
style: {position: 'relative'},
className: props.className + (props.disabled ? ' disabled' : ''),
onMouseDown: this._onSliderMouseDown,
onClick: this._onSliderClick
},
bars,
handles
)
);
}
});
return ReactSlider;
}));