react-slider
Advanced tools
+52
| # 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 | ||
| [](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. |
-164
| <!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> |
-884
| (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; | ||
| })); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
81643
116.71%2
-33.33%14
180%1814
144.8%1
-66.67%14
Infinity%22
-86.59%20
900%2
Infinity%