Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

533 строки
16 KiB

  1. import _defineProperty from 'babel-runtime/helpers/defineProperty';
  2. import _extends from 'babel-runtime/helpers/extends';
  3. import _toConsumableArray from 'babel-runtime/helpers/toConsumableArray';
  4. import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
  5. import _createClass from 'babel-runtime/helpers/createClass';
  6. import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
  7. import _inherits from 'babel-runtime/helpers/inherits';
  8. /* eslint-disable react/prop-types */
  9. import React from 'react';
  10. import PropTypes from 'prop-types';
  11. import classNames from 'classnames';
  12. import { polyfill } from 'react-lifecycles-compat';
  13. import shallowEqual from 'shallowequal';
  14. import Track from './common/Track';
  15. import createSlider from './common/createSlider';
  16. import * as utils from './utils';
  17. var _trimAlignValue = function _trimAlignValue(_ref) {
  18. var value = _ref.value,
  19. handle = _ref.handle,
  20. bounds = _ref.bounds,
  21. props = _ref.props;
  22. var allowCross = props.allowCross,
  23. pushable = props.pushable;
  24. var thershold = Number(pushable);
  25. var valInRange = utils.ensureValueInRange(value, props);
  26. var valNotConflict = valInRange;
  27. if (!allowCross && handle != null && bounds !== undefined) {
  28. if (handle > 0 && valInRange <= bounds[handle - 1] + thershold) {
  29. valNotConflict = bounds[handle - 1] + thershold;
  30. }
  31. if (handle < bounds.length - 1 && valInRange >= bounds[handle + 1] - thershold) {
  32. valNotConflict = bounds[handle + 1] - thershold;
  33. }
  34. }
  35. return utils.ensureValuePrecision(valNotConflict, props);
  36. };
  37. var Range = function (_React$Component) {
  38. _inherits(Range, _React$Component);
  39. function Range(props) {
  40. _classCallCheck(this, Range);
  41. var _this = _possibleConstructorReturn(this, (Range.__proto__ || Object.getPrototypeOf(Range)).call(this, props));
  42. _this.onEnd = function (force) {
  43. var handle = _this.state.handle;
  44. _this.removeDocumentEvents();
  45. if (handle !== null || force) {
  46. _this.props.onAfterChange(_this.getValue());
  47. }
  48. _this.setState({
  49. handle: null
  50. });
  51. };
  52. var count = props.count,
  53. min = props.min,
  54. max = props.max;
  55. var initialValue = Array.apply(undefined, _toConsumableArray(Array(count + 1))).map(function () {
  56. return min;
  57. });
  58. var defaultValue = 'defaultValue' in props ? props.defaultValue : initialValue;
  59. var value = props.value !== undefined ? props.value : defaultValue;
  60. var bounds = value.map(function (v, i) {
  61. return _trimAlignValue({
  62. value: v,
  63. handle: i,
  64. props: props
  65. });
  66. });
  67. var recent = bounds[0] === max ? 0 : bounds.length - 1;
  68. _this.state = {
  69. handle: null,
  70. recent: recent,
  71. bounds: bounds
  72. };
  73. return _this;
  74. }
  75. _createClass(Range, [{
  76. key: 'componentDidUpdate',
  77. value: function componentDidUpdate(prevProps, prevState) {
  78. var _this2 = this;
  79. if (!('value' in this.props || 'min' in this.props || 'max' in this.props)) {
  80. return;
  81. }
  82. if (this.props.min === prevProps.min && this.props.max === prevProps.max && shallowEqual(this.props.value, prevProps.value)) {
  83. return;
  84. }
  85. var _props = this.props,
  86. onChange = _props.onChange,
  87. value = _props.value;
  88. var currentValue = value || prevState.bounds;
  89. if (currentValue.some(function (v) {
  90. return utils.isValueOutOfRange(v, _this2.props);
  91. })) {
  92. var newValues = currentValue.map(function (v) {
  93. return utils.ensureValueInRange(v, _this2.props);
  94. });
  95. onChange(newValues);
  96. }
  97. }
  98. }, {
  99. key: 'onChange',
  100. value: function onChange(state) {
  101. var props = this.props;
  102. var isNotControlled = !('value' in props);
  103. if (isNotControlled) {
  104. this.setState(state);
  105. } else {
  106. var controlledState = {};
  107. ['handle', 'recent'].forEach(function (item) {
  108. if (state[item] !== undefined) {
  109. controlledState[item] = state[item];
  110. }
  111. });
  112. if (Object.keys(controlledState).length) {
  113. this.setState(controlledState);
  114. }
  115. }
  116. var data = _extends({}, this.state, state);
  117. var changedValue = data.bounds;
  118. props.onChange(changedValue);
  119. }
  120. }, {
  121. key: 'onStart',
  122. value: function onStart(position) {
  123. var props = this.props;
  124. var state = this.state;
  125. var bounds = this.getValue();
  126. props.onBeforeChange(bounds);
  127. var value = this.calcValueByPos(position);
  128. this.startValue = value;
  129. this.startPosition = position;
  130. var closestBound = this.getClosestBound(value);
  131. this.prevMovedHandleIndex = this.getBoundNeedMoving(value, closestBound);
  132. this.setState({
  133. handle: this.prevMovedHandleIndex,
  134. recent: this.prevMovedHandleIndex
  135. });
  136. var prevValue = bounds[this.prevMovedHandleIndex];
  137. if (value === prevValue) return;
  138. var nextBounds = [].concat(_toConsumableArray(state.bounds));
  139. nextBounds[this.prevMovedHandleIndex] = value;
  140. this.onChange({ bounds: nextBounds });
  141. }
  142. }, {
  143. key: 'onMove',
  144. value: function onMove(e, position) {
  145. utils.pauseEvent(e);
  146. var state = this.state;
  147. var value = this.calcValueByPos(position);
  148. var oldValue = state.bounds[state.handle];
  149. if (value === oldValue) return;
  150. this.moveTo(value);
  151. }
  152. }, {
  153. key: 'onKeyboard',
  154. value: function onKeyboard(e) {
  155. var _props2 = this.props,
  156. reverse = _props2.reverse,
  157. vertical = _props2.vertical;
  158. var valueMutator = utils.getKeyboardValueMutator(e, vertical, reverse);
  159. if (valueMutator) {
  160. utils.pauseEvent(e);
  161. var state = this.state,
  162. props = this.props;
  163. var bounds = state.bounds,
  164. handle = state.handle;
  165. var oldValue = bounds[handle === null ? state.recent : handle];
  166. var mutatedValue = valueMutator(oldValue, props);
  167. var value = _trimAlignValue({
  168. value: mutatedValue,
  169. handle: handle,
  170. bounds: state.bounds,
  171. props: props
  172. });
  173. if (value === oldValue) return;
  174. var isFromKeyboardEvent = true;
  175. this.moveTo(value, isFromKeyboardEvent);
  176. }
  177. }
  178. }, {
  179. key: 'getValue',
  180. value: function getValue() {
  181. return this.state.bounds;
  182. }
  183. }, {
  184. key: 'getClosestBound',
  185. value: function getClosestBound(value) {
  186. var bounds = this.state.bounds;
  187. var closestBound = 0;
  188. for (var i = 1; i < bounds.length - 1; ++i) {
  189. if (value >= bounds[i]) {
  190. closestBound = i;
  191. }
  192. }
  193. if (Math.abs(bounds[closestBound + 1] - value) < Math.abs(bounds[closestBound] - value)) {
  194. closestBound += 1;
  195. }
  196. return closestBound;
  197. }
  198. }, {
  199. key: 'getBoundNeedMoving',
  200. value: function getBoundNeedMoving(value, closestBound) {
  201. var _state = this.state,
  202. bounds = _state.bounds,
  203. recent = _state.recent;
  204. var boundNeedMoving = closestBound;
  205. var isAtTheSamePoint = bounds[closestBound + 1] === bounds[closestBound];
  206. if (isAtTheSamePoint && bounds[recent] === bounds[closestBound]) {
  207. boundNeedMoving = recent;
  208. }
  209. if (isAtTheSamePoint && value !== bounds[closestBound + 1]) {
  210. boundNeedMoving = value < bounds[closestBound + 1] ? closestBound : closestBound + 1;
  211. }
  212. return boundNeedMoving;
  213. }
  214. }, {
  215. key: 'getLowerBound',
  216. value: function getLowerBound() {
  217. return this.state.bounds[0];
  218. }
  219. }, {
  220. key: 'getUpperBound',
  221. value: function getUpperBound() {
  222. var bounds = this.state.bounds;
  223. return bounds[bounds.length - 1];
  224. }
  225. /**
  226. * Returns an array of possible slider points, taking into account both
  227. * `marks` and `step`. The result is cached.
  228. */
  229. }, {
  230. key: 'getPoints',
  231. value: function getPoints() {
  232. var _props3 = this.props,
  233. marks = _props3.marks,
  234. step = _props3.step,
  235. min = _props3.min,
  236. max = _props3.max;
  237. var cache = this._getPointsCache;
  238. if (!cache || cache.marks !== marks || cache.step !== step) {
  239. var pointsObject = _extends({}, marks);
  240. if (step !== null) {
  241. for (var point = min; point <= max; point += step) {
  242. pointsObject[point] = point;
  243. }
  244. }
  245. var points = Object.keys(pointsObject).map(parseFloat);
  246. points.sort(function (a, b) {
  247. return a - b;
  248. });
  249. this._getPointsCache = { marks: marks, step: step, points: points };
  250. }
  251. return this._getPointsCache.points;
  252. }
  253. }, {
  254. key: 'moveTo',
  255. value: function moveTo(value, isFromKeyboardEvent) {
  256. var _this3 = this;
  257. var state = this.state,
  258. props = this.props;
  259. var nextBounds = [].concat(_toConsumableArray(state.bounds));
  260. var handle = state.handle === null ? state.recent : state.handle;
  261. nextBounds[handle] = value;
  262. var nextHandle = handle;
  263. if (props.pushable !== false) {
  264. this.pushSurroundingHandles(nextBounds, nextHandle);
  265. } else if (props.allowCross) {
  266. nextBounds.sort(function (a, b) {
  267. return a - b;
  268. });
  269. nextHandle = nextBounds.indexOf(value);
  270. }
  271. this.onChange({
  272. recent: nextHandle,
  273. handle: nextHandle,
  274. bounds: nextBounds
  275. });
  276. if (isFromKeyboardEvent) {
  277. // known problem: because setState is async,
  278. // so trigger focus will invoke handler's onEnd and another handler's onStart too early,
  279. // cause onBeforeChange and onAfterChange receive wrong value.
  280. // here use setState callback to hack,but not elegant
  281. this.props.onAfterChange(nextBounds);
  282. this.setState({}, function () {
  283. _this3.handlesRefs[nextHandle].focus();
  284. });
  285. this.onEnd();
  286. }
  287. }
  288. }, {
  289. key: 'pushSurroundingHandles',
  290. value: function pushSurroundingHandles(bounds, handle) {
  291. var value = bounds[handle];
  292. var threshold = this.props.pushable;
  293. threshold = Number(threshold);
  294. var direction = 0;
  295. if (bounds[handle + 1] - value < threshold) {
  296. direction = +1; // push to right
  297. }
  298. if (value - bounds[handle - 1] < threshold) {
  299. direction = -1; // push to left
  300. }
  301. if (direction === 0) {
  302. return;
  303. }
  304. var nextHandle = handle + direction;
  305. var diffToNext = direction * (bounds[nextHandle] - value);
  306. if (!this.pushHandle(bounds, nextHandle, direction, threshold - diffToNext)) {
  307. // revert to original value if pushing is impossible
  308. bounds[handle] = bounds[nextHandle] - direction * threshold;
  309. }
  310. }
  311. }, {
  312. key: 'pushHandle',
  313. value: function pushHandle(bounds, handle, direction, amount) {
  314. var originalValue = bounds[handle];
  315. var currentValue = bounds[handle];
  316. while (direction * (currentValue - originalValue) < amount) {
  317. if (!this.pushHandleOnePoint(bounds, handle, direction)) {
  318. // can't push handle enough to create the needed `amount` gap, so we
  319. // revert its position to the original value
  320. bounds[handle] = originalValue;
  321. return false;
  322. }
  323. currentValue = bounds[handle];
  324. }
  325. // the handle was pushed enough to create the needed `amount` gap
  326. return true;
  327. }
  328. }, {
  329. key: 'pushHandleOnePoint',
  330. value: function pushHandleOnePoint(bounds, handle, direction) {
  331. var points = this.getPoints();
  332. var pointIndex = points.indexOf(bounds[handle]);
  333. var nextPointIndex = pointIndex + direction;
  334. if (nextPointIndex >= points.length || nextPointIndex < 0) {
  335. // reached the minimum or maximum available point, can't push anymore
  336. return false;
  337. }
  338. var nextHandle = handle + direction;
  339. var nextValue = points[nextPointIndex];
  340. var threshold = this.props.pushable;
  341. var diffToNext = direction * (bounds[nextHandle] - nextValue);
  342. if (!this.pushHandle(bounds, nextHandle, direction, threshold - diffToNext)) {
  343. // couldn't push next handle, so we won't push this one either
  344. return false;
  345. }
  346. // push the handle
  347. bounds[handle] = nextValue;
  348. return true;
  349. }
  350. }, {
  351. key: 'trimAlignValue',
  352. value: function trimAlignValue(value) {
  353. var _state2 = this.state,
  354. handle = _state2.handle,
  355. bounds = _state2.bounds;
  356. return _trimAlignValue({
  357. value: value,
  358. handle: handle,
  359. bounds: bounds,
  360. props: this.props
  361. });
  362. }
  363. }, {
  364. key: 'render',
  365. value: function render() {
  366. var _this4 = this;
  367. var _state3 = this.state,
  368. handle = _state3.handle,
  369. bounds = _state3.bounds;
  370. var _props4 = this.props,
  371. prefixCls = _props4.prefixCls,
  372. vertical = _props4.vertical,
  373. included = _props4.included,
  374. disabled = _props4.disabled,
  375. min = _props4.min,
  376. max = _props4.max,
  377. reverse = _props4.reverse,
  378. handleGenerator = _props4.handle,
  379. trackStyle = _props4.trackStyle,
  380. handleStyle = _props4.handleStyle,
  381. tabIndex = _props4.tabIndex;
  382. var offsets = bounds.map(function (v) {
  383. return _this4.calcOffset(v);
  384. });
  385. var handleClassName = prefixCls + '-handle';
  386. var handles = bounds.map(function (v, i) {
  387. var _classNames;
  388. var _tabIndex = tabIndex[i] || 0;
  389. if (disabled || tabIndex[i] === null) {
  390. _tabIndex = null;
  391. }
  392. return handleGenerator({
  393. className: classNames((_classNames = {}, _defineProperty(_classNames, handleClassName, true), _defineProperty(_classNames, handleClassName + '-' + (i + 1), true), _classNames)),
  394. prefixCls: prefixCls,
  395. vertical: vertical,
  396. offset: offsets[i],
  397. value: v,
  398. dragging: handle === i,
  399. index: i,
  400. tabIndex: _tabIndex,
  401. min: min,
  402. max: max,
  403. reverse: reverse,
  404. disabled: disabled,
  405. style: handleStyle[i],
  406. ref: function ref(h) {
  407. return _this4.saveHandle(i, h);
  408. }
  409. });
  410. });
  411. var tracks = bounds.slice(0, -1).map(function (_, index) {
  412. var _classNames2;
  413. var i = index + 1;
  414. var trackClassName = classNames((_classNames2 = {}, _defineProperty(_classNames2, prefixCls + '-track', true), _defineProperty(_classNames2, prefixCls + '-track-' + i, true), _classNames2));
  415. return React.createElement(Track, {
  416. className: trackClassName,
  417. vertical: vertical,
  418. reverse: reverse,
  419. included: included,
  420. offset: offsets[i - 1],
  421. length: offsets[i] - offsets[i - 1],
  422. style: trackStyle[index],
  423. key: i
  424. });
  425. });
  426. return { tracks: tracks, handles: handles };
  427. }
  428. }], [{
  429. key: 'getDerivedStateFromProps',
  430. value: function getDerivedStateFromProps(props, state) {
  431. if ('value' in props || 'min' in props || 'max' in props) {
  432. var value = props.value || state.bounds;
  433. var nextBounds = value.map(function (v, i) {
  434. return _trimAlignValue({
  435. value: v,
  436. handle: i,
  437. bounds: state.bounds,
  438. props: props
  439. });
  440. });
  441. if (nextBounds.length === state.bounds.length && nextBounds.every(function (v, i) {
  442. return v === state.bounds[i];
  443. })) {
  444. return null;
  445. }
  446. return _extends({}, state, {
  447. bounds: nextBounds
  448. });
  449. }
  450. return null;
  451. }
  452. }]);
  453. return Range;
  454. }(React.Component);
  455. Range.displayName = 'Range';
  456. Range.propTypes = {
  457. autoFocus: PropTypes.bool,
  458. defaultValue: PropTypes.arrayOf(PropTypes.number),
  459. value: PropTypes.arrayOf(PropTypes.number),
  460. count: PropTypes.number,
  461. pushable: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
  462. allowCross: PropTypes.bool,
  463. disabled: PropTypes.bool,
  464. reverse: PropTypes.bool,
  465. tabIndex: PropTypes.arrayOf(PropTypes.number),
  466. min: PropTypes.number,
  467. max: PropTypes.number
  468. };
  469. Range.defaultProps = {
  470. count: 1,
  471. allowCross: true,
  472. pushable: false,
  473. tabIndex: []
  474. };
  475. polyfill(Range);
  476. export default createSlider(Range);