import _defineProperty from 'babel-runtime/helpers/defineProperty'; import _toConsumableArray from 'babel-runtime/helpers/toConsumableArray'; import _extends from 'babel-runtime/helpers/extends'; import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import _createClass from 'babel-runtime/helpers/createClass'; import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; import _inherits from 'babel-runtime/helpers/inherits'; /* eslint-disable react/prop-types */ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import shallowEqual from 'shallowequal'; import Track from './common/Track'; import createSlider from './common/createSlider'; import * as utils from './utils'; var Range = function (_React$Component) { _inherits(Range, _React$Component); function Range(props) { _classCallCheck(this, Range); var _this = _possibleConstructorReturn(this, (Range.__proto__ || Object.getPrototypeOf(Range)).call(this, props)); _this.onEnd = function () { _this.setState({ handle: null }); _this.removeDocumentEvents(); _this.props.onAfterChange(_this.getValue()); }; var count = props.count, min = props.min, max = props.max; var initialValue = Array.apply(null, Array(count + 1)).map(function () { return min; }); var defaultValue = 'defaultValue' in props ? props.defaultValue : initialValue; var value = props.value !== undefined ? props.value : defaultValue; var bounds = value.map(function (v) { return _this.trimAlignValue(v); }); var recent = bounds[0] === max ? 0 : bounds.length - 1; _this.state = { handle: null, recent: recent, bounds: bounds }; return _this; } _createClass(Range, [{ key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { var _this2 = this; if (!('value' in nextProps || 'min' in nextProps || 'max' in nextProps)) return; if (this.props.min === nextProps.min && this.props.max === nextProps.max && shallowEqual(this.props.value, nextProps.value)) { return; } var bounds = this.state.bounds; var value = nextProps.value || bounds; var nextBounds = value.map(function (v) { return _this2.trimAlignValue(v, nextProps); }); if (nextBounds.length === bounds.length && nextBounds.every(function (v, i) { return v === bounds[i]; })) return; this.setState({ bounds: nextBounds }); if (bounds.some(function (v) { return utils.isValueOutOfRange(v, nextProps); })) { this.props.onChange(nextBounds); } } }, { key: 'onChange', value: function onChange(state) { var props = this.props; var isNotControlled = !('value' in props); if (isNotControlled) { this.setState(state); } else if (state.handle !== undefined) { this.setState({ handle: state.handle }); } var data = _extends({}, this.state, state); var changedValue = data.bounds; props.onChange(changedValue); } }, { key: 'onStart', value: function onStart(position) { var props = this.props; var state = this.state; var bounds = this.getValue(); props.onBeforeChange(bounds); var value = this.calcValueByPos(position); this.startValue = value; this.startPosition = position; var closestBound = this.getClosestBound(value); var boundNeedMoving = this.getBoundNeedMoving(value, closestBound); this.setState({ handle: boundNeedMoving, recent: boundNeedMoving }); var prevValue = bounds[boundNeedMoving]; if (value === prevValue) return; var nextBounds = [].concat(_toConsumableArray(state.bounds)); nextBounds[boundNeedMoving] = value; this.onChange({ bounds: nextBounds }); } }, { key: 'onMove', value: function onMove(e, position) { utils.pauseEvent(e); var props = this.props; var state = this.state; var value = this.calcValueByPos(position); var oldValue = state.bounds[state.handle]; if (value === oldValue) return; var nextBounds = [].concat(_toConsumableArray(state.bounds)); nextBounds[state.handle] = value; var nextHandle = state.handle; if (props.pushable !== false) { var originalValue = state.bounds[nextHandle]; this.pushSurroundingHandles(nextBounds, nextHandle, originalValue); } else if (props.allowCross) { nextBounds.sort(function (a, b) { return a - b; }); nextHandle = nextBounds.indexOf(value); } this.onChange({ handle: nextHandle, bounds: nextBounds }); } }, { key: 'getValue', value: function getValue() { return this.state.bounds; } }, { key: 'getClosestBound', value: function getClosestBound(value) { var bounds = this.state.bounds; var closestBound = 0; for (var i = 1; i < bounds.length - 1; ++i) { if (value > bounds[i]) { closestBound = i; } } if (Math.abs(bounds[closestBound + 1] - value) < Math.abs(bounds[closestBound] - value)) { closestBound = closestBound + 1; } return closestBound; } }, { key: 'getBoundNeedMoving', value: function getBoundNeedMoving(value, closestBound) { var _state = this.state, bounds = _state.bounds, recent = _state.recent; var boundNeedMoving = closestBound; var isAtTheSamePoint = bounds[closestBound + 1] === bounds[closestBound]; if (isAtTheSamePoint) { boundNeedMoving = recent; } if (isAtTheSamePoint && value !== bounds[closestBound + 1]) { boundNeedMoving = value < bounds[closestBound + 1] ? closestBound : closestBound + 1; } return boundNeedMoving; } }, { key: 'getLowerBound', value: function getLowerBound() { return this.state.bounds[0]; } }, { key: 'getUpperBound', value: function getUpperBound() { var bounds = this.state.bounds; return bounds[bounds.length - 1]; } /** * Returns an array of possible slider points, taking into account both * `marks` and `step`. The result is cached. */ }, { key: 'getPoints', value: function getPoints() { var _props = this.props, marks = _props.marks, step = _props.step, min = _props.min, max = _props.max; var cache = this._getPointsCache; if (!cache || cache.marks !== marks || cache.step !== step) { var pointsObject = _extends({}, marks); if (step !== null) { for (var point = min; point <= max; point += step) { pointsObject[point] = point; } } var points = Object.keys(pointsObject).map(parseFloat); points.sort(function (a, b) { return a - b; }); this._getPointsCache = { marks: marks, step: step, points: points }; } return this._getPointsCache.points; } }, { key: 'pushSurroundingHandles', value: function pushSurroundingHandles(bounds, handle, originalValue) { var threshold = this.props.pushable; var value = bounds[handle]; var direction = 0; if (bounds[handle + 1] - value < threshold) { direction = +1; // push to right } if (value - bounds[handle - 1] < threshold) { direction = -1; // push to left } if (direction === 0) { return; } var nextHandle = handle + direction; var diffToNext = direction * (bounds[nextHandle] - value); if (!this.pushHandle(bounds, nextHandle, direction, threshold - diffToNext)) { // revert to original value if pushing is impossible bounds[handle] = originalValue; } } }, { key: 'pushHandle', value: function pushHandle(bounds, handle, direction, amount) { var originalValue = bounds[handle]; var currentValue = bounds[handle]; while (direction * (currentValue - originalValue) < amount) { if (!this.pushHandleOnePoint(bounds, handle, direction)) { // can't push handle enough to create the needed `amount` gap, so we // revert its position to the original value bounds[handle] = originalValue; return false; } currentValue = bounds[handle]; } // the handle was pushed enough to create the needed `amount` gap return true; } }, { key: 'pushHandleOnePoint', value: function pushHandleOnePoint(bounds, handle, direction) { var points = this.getPoints(); var pointIndex = points.indexOf(bounds[handle]); var nextPointIndex = pointIndex + direction; if (nextPointIndex >= points.length || nextPointIndex < 0) { // reached the minimum or maximum available point, can't push anymore return false; } var nextHandle = handle + direction; var nextValue = points[nextPointIndex]; var threshold = this.props.pushable; var diffToNext = direction * (bounds[nextHandle] - nextValue); if (!this.pushHandle(bounds, nextHandle, direction, threshold - diffToNext)) { // couldn't push next handle, so we won't push this one either return false; } // push the handle bounds[handle] = nextValue; return true; } }, { key: 'trimAlignValue', value: function trimAlignValue(v) { var nextProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var mergedProps = _extends({}, this.props, nextProps); var valInRange = utils.ensureValueInRange(v, mergedProps); var valNotConflict = this.ensureValueNotConflict(valInRange, mergedProps); return utils.ensureValuePrecision(valNotConflict, mergedProps); } }, { key: 'ensureValueNotConflict', value: function ensureValueNotConflict(val, _ref) { var allowCross = _ref.allowCross; var state = this.state || {}; var handle = state.handle, bounds = state.bounds; /* eslint-disable eqeqeq */ if (!allowCross && handle != null) { if (handle > 0 && val <= bounds[handle - 1]) { return bounds[handle - 1]; } if (handle < bounds.length - 1 && val >= bounds[handle + 1]) { return bounds[handle + 1]; } } /* eslint-enable eqeqeq */ return val; } }, { key: 'render', value: function render() { var _this3 = this; var _state2 = this.state, handle = _state2.handle, bounds = _state2.bounds; var _props2 = this.props, prefixCls = _props2.prefixCls, vertical = _props2.vertical, included = _props2.included, disabled = _props2.disabled, min = _props2.min, max = _props2.max, handleGenerator = _props2.handle, trackStyle = _props2.trackStyle, handleStyle = _props2.handleStyle; var offsets = bounds.map(function (v) { return _this3.calcOffset(v); }); var handleClassName = prefixCls + '-handle'; var handles = bounds.map(function (v, i) { var _classNames; return handleGenerator({ className: classNames((_classNames = {}, _defineProperty(_classNames, handleClassName, true), _defineProperty(_classNames, handleClassName + '-' + (i + 1), true), _classNames)), vertical: vertical, offset: offsets[i], value: v, dragging: handle === i, index: i, min: min, max: max, disabled: disabled, style: handleStyle[i], ref: function ref(h) { return _this3.saveHandle(i, h); } }); }); var tracks = bounds.slice(0, -1).map(function (_, index) { var _classNames2; var i = index + 1; var trackClassName = classNames((_classNames2 = {}, _defineProperty(_classNames2, prefixCls + '-track', true), _defineProperty(_classNames2, prefixCls + '-track-' + i, true), _classNames2)); return React.createElement(Track, { className: trackClassName, vertical: vertical, included: included, offset: offsets[i - 1], length: offsets[i] - offsets[i - 1], style: trackStyle[index], key: i }); }); return { tracks: tracks, handles: handles }; } }]); return Range; }(React.Component); Range.displayName = 'Range'; Range.propTypes = { defaultValue: PropTypes.arrayOf(PropTypes.number), value: PropTypes.arrayOf(PropTypes.number), count: PropTypes.number, pushable: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]), allowCross: PropTypes.bool, disabled: PropTypes.bool }; Range.defaultProps = { count: 1, allowCross: true, pushable: false }; export default createSlider(Range);