Compare commits

...
97 Commits
Author SHA1 Message Date
Benjy Cui 53aa61faf2 feat: provide dist files 2016-11-15 14:04:15 +08:00
Benjy Cui 9655483bf5 bump 5.2.1 2016-10-31 11:51:44 +08:00
Benjy Cui faa4f384a3 fix: handle could be 0, close: #163 2016-10-31 11:50:49 +08:00
Benjy Cui 18ddbb796c bump 5.2.0 2016-10-31 11:24:55 +08:00
Benjy Cui 051fef6603 feat: make clickable area larger, ref: #160 2016-10-31 11:23:41 +08:00
Benjy Cui b325daef48 bump 5.1.3 2016-10-21 16:44:58 +08:00
Benjy Cui db47c3d27d chore: improve UX, close: #160 2016-10-21 16:44:32 +08:00
Benjy Cui f20014a9d4 bump 5.1.2 2016-10-14 15:10:14 +08:00
Paul Taylor 6074d5fc43 fix: Account for position difference between handle and event origin when dragging (#146)
* Adjust handle drag position to account for the difference between handle origin and mouse or touch event origin.

* Add slider dragOffset tests.
2016-10-14 15:07:51 +08:00
Benjy Cui 34754440ed docs: add change log for 5.0.0, close: #149 2016-10-14 14:45:53 +08:00
Benjy Cui fac5a1b9f3 bump 5.1.1 2016-09-12 11:33:18 +08:00
Benjy Cui 1b8e27e613 bump 5.1.0 2016-09-12 11:28:41 +08:00
Benjy Cui e438757a64 feat: support tooltipPrefixcls 2016-09-12 11:26:48 +08:00
Benjy Cui aad5fef20c bump 5.0.0 2016-09-12 10:57:20 +08:00
Ulrich-Matthias Schäfer 1568019a59 fix: onChange which fired on every mousmove, added test (#145) 2016-09-12 10:54:16 +08:00
Dylan Kirkby abdb297c16 chore: Add warning when range isn't a multiple of the step (#142) 2016-09-12 10:53:15 +08:00
Benjy Cui 67b76953c6 chore: update .travis.yml 2016-09-10 17:04:06 +08:00
Benjy Cui b6d8f389ef fix: should limit CSS scope, close: #147 2016-09-10 16:52:38 +08:00
Benjy Cui d7e6b9d9be bump 4.0.1 2016-08-16 10:01:30 +08:00
Benjy Cui 67a33126ec style: update code style to please lint 2016-08-16 09:59:14 +08:00
Derrick Pelletier 1a8331bcf1 refactor: Removed z-indexes #135 (#136) 2016-08-16 09:32:21 +08:00
Benjy Cui f55b2ec283 bump 4.0.0 2016-08-12 16:05:55 +08:00
Benjy Cui cf45d688d9 chore: update rc-tools 2016-08-12 16:03:06 +08:00
Benjy Cui bc2da18f46 style: update code style 2016-08-12 15:40:06 +08:00
Benjy Cui 8fd94b4eb1 refactor: use babel-runtime 2016-08-12 15:20:14 +08:00
Hagai Akibayov 4b2f6f27a4 feat: Add multi-range support (#133) 2016-08-12 15:07:48 +08:00
Benjy Cui 36210b3634 bump 3.8.2 2016-07-27 10:17:57 +08:00
Helder Santana 9219927f8a chore: add 'style' node to package.json (#127) 2016-07-27 10:15:55 +08:00
Benjy Cui 826fd73c93 docs: update usage 2016-07-25 14:18:50 +08:00
Benjy Cui a4cf02cf75 bump 3.8.1 2016-07-25 14:09:49 +08:00
Benjy Cui d05761f396 fix: should pass prefixCls to Tooltip correctly, close: #123 2016-07-25 14:09:05 +08:00
Benjy Cui 926d5aeb91 bump 3.8.0 2016-07-20 09:40:04 +08:00
miskreant b2ae1bc0fc feat: identify upper/lower range handles (#118)
Extend the handle classNames, and provide props so that
upper/lower handles are easily distinguishable in CSS & React.

* Use classNames() instead of string concatenation
2016-07-20 09:34:49 +08:00
Benjy Cui 80d4ee4794 bump 3.7.4 2016-07-11 09:55:12 +08:00
Benjy Cui e2504d99fe fix: should calc cloest point correctly, close: #116 2016-07-11 09:53:38 +08:00
Benjy Cui d4ae5a5298 deps: update 2016-07-11 09:48:44 +08:00
Benjy Cui ce755ecb4b bump 3.7.3 to publish website 2016-06-29 16:23:00 +08:00
Benjy Cui 9bc56e993d chore: update demo 2016-06-29 16:22:01 +08:00
Benjy Cui 1e34b94d86 bump 3.7.2 2016-06-22 09:25:13 +08:00
Aaron Lampros a678437462 fix: import dom util directly, close: #108. (#110) 2016-06-22 09:20:57 +08:00
Benjy Cui a4c06c2509 bump 3.7.1 2016-06-12 09:17:48 +08:00
Jordan Stephens ff9e68cd36 ignore right button on mousedown (#107)
fix: should ignore right button on mousedown
2016-06-12 09:16:24 +08:00
Benjy Cui 1adc68504a bump 3.7.0 2016-05-31 14:27:23 +08:00
Benjy Cui 01174f3cb0 Merge pull request #102 from elboman/custom-handle
Allow to pass custom handle components
2016-05-31 14:24:13 +08:00
Marco Botto 1ae39dd995 added custom handle API to docs 2016-05-30 12:16:53 +02:00
Marco Botto 0b4b546e03 changed API to from to 2016-05-30 12:08:52 +02:00
Marco Botto bc3883304d allow to pass custom handle components 2016-05-16 17:38:56 +02:00
Benjy Cui e2f9c508f3 Merge pull request #101 from nathandurand/master
Add license file
2016-05-06 12:05:19 +08:00
Nathan Durand ef5d136b14 Update license file 2016-05-06 06:53:38 +03:00
Nathan Durand f1637da046 Add license file 2016-05-05 16:41:49 +03:00
Benjy Cui ddcf32b094 bump 3.6.2 2016-04-28 09:35:21 +08:00
Boris Serdiuk 025a9e7ea6 Merge pull request #99 from react-component/fix-bugs
Fix #96 and #97
2016-04-27 15:08:48 +04:00
Benjy Cui e4396ae4bd refactor: simplify code 2016-04-27 17:48:17 +08:00
Benjy Cui 37b7f508da fix: use the latest step, close: #96 2016-04-27 14:29:45 +08:00
Benjy Cui ecbb253986 fix: set recent default value correctly, close: #97 2016-04-27 14:17:32 +08:00
Benjy Cui f4a82e3fd1 bump 3.6.1 2016-04-27 12:09:04 +08:00
Benjy Cui 0b1a421a71 chore: add warning for invalid usage, close: #91 2016-04-27 12:07:20 +08:00
Benjy Cui 0e6fed5c9b Merge pull request #85 from react-component/feat-vertical
feat: vertical slider
2016-04-10 08:53:55 +08:00
叶言 1ce8c3b117 fix: eslint error 2016-04-09 13:32:44 +08:00
叶言 ffdd062074 fix: some diffs
- update to 3.6.0 (refs:https://github.com/react-component/slider/pull/85#discussion-diff-58325891)
- change `.rc-slider-vertical` to `.{prefixClass}-vertical` (refs:https://github.com/react-component/slider/pull/85#discussion-diff-58325944)
- put all the slider & Range in one row (refs:https://github.com/react-component/slider/pull/85#discussion-diff-58326190)
2016-04-09 13:04:52 +08:00
叶言 3b03957a54 feat: vertical slider
- add `vertical` props
- add vertical slider demo: v-marks.html v-range.html v-slider.html

refs:https://github.com/react-component/slider/issues/18
2016-04-01 16:40:36 +08:00
Benjy Cui e1be65a107 docs: update demo 2016-03-23 11:48:36 +08:00
Benjy Cui 68d3efc7da deps: remove object-assign 2016-03-23 10:54:54 +08:00
Benjy Cui 806cb06d76 bump 3.5.1 2016-03-22 17:22:43 +08:00
Benjy Cui 73b9953084 refactor: update Marks style algorithm 2016-03-22 17:21:50 +08:00
Benjy Cui 6347b6d0c6 bump 3.5.0 2016-03-22 16:55:02 +08:00
Benjy Cui 6a5573b6ca feat: support React.Component in marks 2016-03-22 16:53:38 +08:00
Benjy Cui e030ba9f9d refactor: rename Dots to Steps 2016-03-22 16:27:50 +08:00
Benjy Cui daaea139b9 bump 3.4.0 2016-03-17 15:48:33 +08:00
Boris Serdiuk 2641880783 Merge pull request #82 from react-component/feat-mark-style
feat: support set style of specific mark point
2016-03-17 10:30:45 +03:00
Benjy Cui d6e81a692c feat: support set style of specific mark point 2016-03-17 09:45:55 +08:00
Benjy Cui 0512ee6ee0 Merge pull request #71 from kalinichenko/master
put marks in correct order so one can style it
2016-02-02 20:53:13 +08:00
Andrey Kalinichenko 613a2ae213 put marks in correct order so one can style it 2016-02-02 11:35:10 +01:00
yiminghe 2cb0dbce6f fix: work with rc-form 2016-01-30 18:52:12 +08:00
Benjy Cui f850dab6c4 bump 3.3.1 2015-12-21 09:59:02 +08:00
Benjy Cui f3c1d4bd3d fix: the key of dots should be unique #64 2015-12-21 09:57:30 +08:00
Benjy Cui 4d795b01cc chore: update unit test 2015-12-18 17:30:34 +08:00
Benjy Cui 959007ed30 bump 3.3.0 2015-12-18 10:11:50 +08:00
Benjy Cui 079469b765 refactor: the default value of tipFormatter 2015-12-18 10:10:52 +08:00
Benjy Cui ddb2bf670b Merge pull request #61 from frank-weindel/hide-the-tip
If tipFormatter is null, don't show the tip
2015-12-18 09:18:41 +08:00
Frank Weindel 4c57f3ecca Refactor isNoTip expression 2015-12-17 10:49:29 -05:00
Frank Weindel 24f3b755b2 Add example for hiding the tooltip 2015-12-17 10:48:54 -05:00
Frank Weindel 867e252631 Add documentation on how to hide the tip 2015-12-16 16:42:13 -05:00
Frank Weindel 39de81dfa9 If tipFormatter is null, don't show the tip 2015-12-16 16:38:15 -05:00
Benjy Cui 978d877323 bump 3.2.0 2015-12-16 09:30:46 +08:00
Boris Serdiuk a1ae3a8374 Merge pull request #60 from react-component/fix-rerender
fix: crash when rerender #57
2015-12-15 13:53:31 +03:00
Benjy Cui 15f568283c fix: crash when rerender #57 2015-12-15 11:32:41 +08:00
Benjy Cui e85d325542 Merge pull request #59 from react-component/feat-allowCross
feat: add `allowCross` property #56
2015-12-15 09:06:09 +08:00
Benjy Cui 395fa6c201 feat: add allowCross property #56 2015-12-12 08:45:28 +08:00
Benjy Cui 0329df0c5a bump 3.1.4 2015-12-07 14:40:57 +08:00
Boris Serdiuk 7fa8dfdd03 Merge pull request #55 from react-component/fix-align
fix: value should be aligned if `min` `max` changed
2015-12-07 09:23:51 +03:00
Benjy Cui 555f5a0660 fix: value should be aligned if min max changed 2015-12-06 09:21:17 +08:00
Benjy Cui c232d62c86 bump 3.1.3 2015-12-02 11:41:32 +08:00
Benjy Cui 1262a78638 deps: replace rcUtil.classSet with classNames 2015-12-02 11:30:34 +08:00
Benjy Cui e7d98e0ee4 chore: update unit test 2015-12-01 17:57:05 +08:00
Benjy Cui 5f9a65137a style: update code style to please lint 2015-12-01 16:26:09 +08:00
Benjy Cui 64296d4957 docs: add docs for onChange onAfterChange 2015-11-24 11:44:44 +08:00
27 changed files with 1473 additions and 363 deletions
+2 -1
View File
@@ -23,4 +23,5 @@ node_modules
dist
assets/**/*.css
build
lib
lib
/coverage
+3 -3
View File
@@ -29,8 +29,8 @@ script:
env:
matrix:
- TEST_TYPE=lint
- TEST_TYPE=browser-test
- TEST_TYPE=browser-test-cover
- TEST_TYPE=test
- TEST_TYPE=coverage
- TEST_TYPE=saucelabs
global:
- secure: S1VwbaPzLnSH/IUT/wlJulxAX5VHRIDmSt53h/ycHcZsszUpWcLCJRQAe0fTVB2dAx5MdBbSZ+o+tr3tRwVB5TRAYm0oTCsYAkOZaWOB28RuUQtdGt3wf9xxTG1UiPiaLLUW3waX9zAaf3yqKBcJGf1op0RD8dksxbCFw/7xVbU=
@@ -39,4 +39,4 @@ env:
matrix:
allow_failures:
- env: "TEST_TYPE=saucelabs"
- env: "TEST_TYPE=saucelabs"
+14
View File
@@ -1,6 +1,20 @@
# History
----
## 5.0.0 / 2016-09-12
[#147](https://github.com/react-component/slider/issues/147) fix style conflicts with rc-tooltip [@benjycui](https://github.com/benjycui)
[#145](https://github.com/react-component/slider/pull/145) fix `onChange` will be triggered while mousemove [@Fuzzyma](https://github.com/Fuzzyma)
## 4.0.0 / 2016-08-12
[#133](https://github.com/react-component/slider/pull/133) support multi-range ([@sosz](https://github.com/sosz))
## 3.6.0 / 2016-04-01
[#18](https://github.com/react-component/slider/issues/18) add `vertical` props ([@wnlee](https://github.com/WNLee))
...
## 1.2.5 / 2015-07-13
+20
View File
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+49 -9
View File
@@ -34,6 +34,8 @@ slider ui component for react
<img src="https://t.alipayobjects.com/images/T1wO8fXd4rXXXXXXXX.png" width="550"/>
<img src="http://i.giphy.com/l46Cs36c9HrHMExoc.gif"/>
## Feature
@@ -50,6 +52,8 @@ slider ui component for react
## Usage
```js
require('rc-slider/assets/index.css');
var React = require('react');
var ReactDOM = require('react-dom');
var Rcslider = require('rc-slider');
@@ -90,9 +94,9 @@ ReactDOM.render(<Rcslider />, container);
</tr>
<tr>
<td>marks</td>
<td>object {number: string}</td>
<td>object{number: string} or object{number: object{ style, label }}</td>
<td>{}</td>
<td>Mark on the slider. The key determines the position, and the value determines what will show.</td>
<td>Mark on the slider. The key determines the position, and the value determines what will show. If you want to set the style of a specific mark point, the value should be an object which contains `style` and `label` properties.</td>
</tr>
<tr>
<td>step</td>
@@ -102,21 +106,45 @@ ReactDOM.render(<Rcslider />, container);
</tr>
<tr>
<td>range</td>
<td>boolean or number</td>
<td>false</td>
<td>Determines the type of slider. If range is `true`, two handles will be rendered in order to select a range. If range is a number, multiple handles will be rendered (number + 1). Using `range={true}` is equivalent to `range={1}`.</td>
</tr>
<tr>
<td>allowCross</td>
<td>boolean</td>
<td>true</td>
<td>When `range` is `true`, `allowCross` could be set as `true` to allow those handles to cross.</td>
</tr>
<tr>
<td>pushable</td>
<td>boolean or number</td>
<td>true</td>
<td>When `range` is `true`, `pushable` could be set as `true` to allow pushing of surrounding handles when moving an handle. When set to a number, the number will be the minimum ensured distance between handles. Example: <img src="http://i.giphy.com/l46Cs36c9HrHMExoc.gif"/></td>
</tr>
<tr>
<td>vertical</td>
<td>boolean</td>
<td>false</td>
<td>Determines the type of slider. If range is `true`, two handles will be rendered in order to select a range.</td>
<td>If vertical is `true`, the slider will be vertical.</td>
</tr>
<tr>
<td>defaultValue</td>
<td>number or [number, number]</td>
<td>number or [number, number, ...]</td>
<td>0 or [0, 0]</td>
<td>Set initial positions of handles. If range is `false`, the type of `defaultValue` should be `number`. Otherwise, `[number, number]`</td>
<td>Set initial positions of handles. If range is `false`, the type of `defaultValue` should be `number`. Otherwise, `[number, number, ...]`</td>
</tr>
<tr>
<td>value</td>
<td>number or [number, number]</td>
<td>number or [number, number, ...]</td>
<td></td>
<td>Set current positions of handles. If range is `false`, the type of `defaultValue` should be `number`. Otherwise, `[number, number]`</td>
<td>Set current positions of handles. If range is `false`, the type of `defaultValue` should be `number`. Otherwise, `[number, number, ...]`</td>
</tr>
<tr>
<td>handle</td>
<td>Component</td>
<td></td>
<td>Provide a custom Handle to use in the slider by passing a component. This component will have a `value` and `offset` props used to define custom styling/content.</td>
</tr>
<tr>
<td>included</td>
@@ -138,9 +166,9 @@ ReactDOM.render(<Rcslider />, container);
</tr>
<tr>
<td>tipFormatter</td>
<td>function</td>
<td>function or `null`</td>
<td></td>
<td>Format the value of the tooltip if it shows.</td>
<td>Format the value of the tooltip if it shows. If `null` the tooltip will always be hidden.</td>
</tr>
<tr>
<td>dots</td>
@@ -148,6 +176,18 @@ ReactDOM.render(<Rcslider />, container);
<td>false</td>
<td>When the `step` value is greater than 1, you can set the `dots` to `true` if you want to render the slider with dots.</td>
</tr>
<tr>
<td>onChange</td>
<td>function</td>
<td>NOOP</td>
<td>`onChange` will be triggered while the value of Slider changing.</td>
</tr>
<tr>
<td>onAfterChange</td>
<td>function</td>
<td>NOOP</td>
<td>`onAfterChange` will be triggered when `ontouchend` or `onmouseup` is triggered.</td>
</tr>
</tbody>
</table>
+59 -12
View File
@@ -23,20 +23,25 @@
.@{prefixClass} {
position: relative;
height: 4px;
height: 14px;
padding: 5px 0;
width: 100%;
border-radius: @border-radius-base;
background-color: #e9e9e9;
.borderBox();
&-rail {
position: absolute;
width: 100%;
background-color: #e9e9e9;
height: 4px;
}
&-track {
position: absolute;
left: 0;
height: 4px;
border-radius: @border-radius-base;
background-color: tint(@primary-color, 60%);
z-index: 1;
}
&-handle {
@@ -49,7 +54,6 @@
border-radius: 50%;
border: solid 2px tint(@primary-color, 50%);
background-color: #fff;
z-index: 2;
&:hover {
border-color: tint(@primary-color, 20%);
@@ -64,11 +68,10 @@
&-mark {
position: absolute;
top: 10px;
top: 18px;
left: 0;
width: 100%;
font-size: 12px;
z-index: 3;
}
&-mark-text {
@@ -89,12 +92,11 @@
width: 100%;
height: 4px;
background: transparent;
z-index: 1;
}
&-dot {
position: absolute;
top: -2px;
bottom: -2px;
margin-left: -4px;
width: 8px;
height: 8px;
@@ -133,6 +135,52 @@
}
}
.@{prefixClass}-vertical {
width: 14px;
height: 100%;
padding: 0 5px;
.@{prefixClass} {
&-rail {
height: 100%;
width: 4px;
}
&-track {
left: 5px;
bottom: 0;
width: 4px;
}
&-handle {
margin-left: -5px;
margin-bottom: -7px;
}
&-mark {
top: 0;
left: 18px;
height: 100%;
}
&-step {
height: 100%;
width: 4px;
}
&-dot {
left: 2px;
margin-bottom: -4px;
&:first-child {
margin-bottom: -4px;
}
&:last-child {
margin-bottom: -4px;
}
}
}
}
.motion-common() {
animation-duration: .3s;
animation-fill-mode: both;
@@ -193,11 +241,10 @@
}
}
.rc-tooltip {
.@{prefixClass}-tooltip {
position: absolute;
left: -9999px;
top: -9999px;
z-index: 4;
visibility: visible;
.borderBox();
@@ -239,4 +286,4 @@
border-width: @tooltip-arrow-width @tooltip-arrow-width 0;
border-top-color: @tooltip-arrow-color;
}
}
}
+1
View File
@@ -0,0 +1 @@
placeholder
+44
View File
@@ -0,0 +1,44 @@
require('rc-slider/assets/index.less');
const React = require('react');
const ReactDOM = require('react-dom');
const Slider = require('rc-slider');
const wrapperStyle = { width: 400, margin: 50 };
const handleStyle = {
position: 'absolute',
transform: 'translate(-50%, -50%)',
cursor: 'pointer',
padding: '2px',
border: '2px solid #abe2fb',
borderRadius: '3px',
background: '#fff',
fontSize: '14px',
textAlign: 'center',
};
const CustomHandle = props => {
const style = Object.assign({ left: `${props.offset}%` }, handleStyle);
return (
<div style={style}>val: {props.value}</div>
);
};
CustomHandle.propTypes = {
value: React.PropTypes.any,
offset: React.PropTypes.number,
};
ReactDOM.render(
<div>
<div style={wrapperStyle}>
<p>Default slider</p>
<Slider min={0} max={20} defaultValue={3} />
</div>
<div style={wrapperStyle}>
<p>Slider with custom handle</p>
<Slider min={0} max={20} defaultValue={3} handle={<CustomHandle />} />
</div>
</div>,
document.getElementById('__react-content')
);
+16 -12
View File
@@ -1,23 +1,27 @@
'use strict';
require('rc-slider/assets/index.less');
var React = require('react');
var ReactDOM = require('react-dom');
var Slider = require('rc-slider');
const React = require('react');
const ReactDOM = require('react-dom');
const Slider = require('rc-slider');
var style = {width: 400, margin: 50};
var marks = {
const style = { width: 400, margin: 50 };
const marks = {
'-10': '-10°C',
0: '0°C',
0: <strong>0°C</strong>,
26: '26°C',
37: '37°C',
100: '100°C'
50: '50°C',
100: {
style: {
color: 'red',
},
label: <strong>100°C</strong>,
},
};
var log = function(value) {
function log(value) {
console.log(value);
};
}
ReactDOM.render(
<div>
@@ -27,7 +31,7 @@ ReactDOM.render(
</div>
<div style={style}>
<p>Slider with marks and steps</p>
<Slider min={-10} marks={marks} step={10} onChange={log} defaultValue={20} />
<Slider dots min={-10} marks={marks} step={10} onChange={log} defaultValue={20} />
</div>
<div style={style}>
+89 -20
View File
@@ -1,42 +1,103 @@
'use strict';
/* eslint react/no-multi-comp: 0 */
require('rc-slider/assets/index.less');
var React = require('react');
var ReactDOM = require('react-dom');
var Slider = require('rc-slider');
const React = require('react');
const ReactDOM = require('react-dom');
const Slider = require('rc-slider');
var style = {width: 400, margin: 50};
var log = function(value) {
const style = { width: 400, margin: 50 };
function log(value) {
console.log(value);
};
}
var CustomizedRange = React.createClass({
getInitialState: function() {
const CustomizedRange = React.createClass({
getInitialState() {
return {
value: [20, 40]
}
lowerBound: 20,
upperBound: 40,
value: [20, 40],
};
},
onChange: function(value) {
onLowerBoundChange(e) {
this.setState({ lowerBound: +e.target.value });
},
onUpperBoundChange(e) {
this.setState({ upperBound: +e.target.value });
},
onSliderChange(value) {
log(value);
this.setState({
value: value
value,
});
},
render: function() {
return <Slider range value={this.state.value} onChange={this.onChange} />;
}
handleApply() {
const { lowerBound, upperBound } = this.state;
this.setState({ value: [lowerBound, upperBound] });
},
render() {
return (
<div>
<label>LowerBound: </label>
<input type="number" value={this.state.lowerBound} onChange={this.onLowerBoundChange} />
<br />
<label>UpperBound: </label>
<input type="number" value={this.state.upperBound} onChange={this.onUpperBoundChange} />
<br />
<button onClick={this.handleApply}>Apply</button>
<br /><br />
<Slider range allowCross={false} value={this.state.value} onChange={this.onSliderChange} />
</div>
);
},
});
const DynamicBounds = React.createClass({
getInitialState() {
return {
min: 0,
max: 100,
};
},
onSliderChange(value) {
log(value);
},
onMinChange(e) {
this.setState({
min: +e.target.value || 0,
});
},
onMaxChange(e) {
this.setState({
max: +e.target.value || 100,
});
},
render() {
return (
<div>
<label>Min: </label>
<input type="number" value={this.state.min} onChange={this.onMinChange} />
<br />
<label>Max: </label>
<input type="number" value={this.state.max} onChange={this.onMaxChange} />
<br /><br />
<Slider range defaultValue={[20, 50]} min={this.state.min} max={this.state.max}
onChange={this.onSliderChange}
/>
</div>
);
},
});
ReactDOM.render(
<div>
<div style={style}>
<p>Basic Range</p>
<Slider range defaultValue={[0, 20]} onChange={log} />
<p>Basic Range`allowCross=false`</p>
<Slider range allowCross={false} defaultValue={[0, 20]} onChange={log} />
</div>
<div style={style}>
<p>Basic Range`step=20` </p>
<Slider range step={20} defaultValue={[20, 40]} onBeforeChange={log} />
<Slider range step={20} defaultValue={[20, 20]} onBeforeChange={log} />
</div>
<div style={style}>
<p>Basic Range`step=20, dots` </p>
@@ -46,9 +107,17 @@ ReactDOM.render(
<p>Controlled Range</p>
<Slider range value={[20, 40]} />
</div>
<div style={style}>
<p>Multi Range</p>
<Slider range={3} value={[20, 40, 60, 80]} />
</div>
<div style={style}>
<p>Customized Range</p>
<CustomizedRange />
</div>
<div style={style}>
<p>Range with dynamic `max` `min`</p>
<DynamicBounds />
</div>
</div>
, document.getElementById('__react-content'));
+74 -20
View File
@@ -1,43 +1,87 @@
'use strict';
/* eslint react/no-multi-comp: 0 */
require('rc-slider/assets/index.less');
var React = require('react');
var ReactDOM = require('react-dom');
var Slider = require('rc-slider');
const React = require('react');
const ReactDOM = require('react-dom');
const Slider = require('rc-slider');
var style = {width: 400, margin: 50};
var log = function(value) {
const style = { width: 400, margin: 50 };
function log(value) {
console.log(value);
};
}
function percentFormatter(v) {
return v + ' %';
return `${v} %`;
}
var CustomizedSlider = React.createClass({
getInitialState: function() {
const CustomizedSlider = React.createClass({
getInitialState() {
return {
value: 50
}
value: 50,
};
},
onChange: function(value) {
onSliderChange(value) {
log(value);
this.setState({
value: value
value,
});
},
render: function() {
return <Slider value={this.state.value} onChange={this.onChange} />;
}
onAfterChange(value) {
console.log(value);
},
render() {
return (
<Slider value={this.state.value}
onChange={this.onSliderChange} onAfterChange={this.onAfterChange}
/>
);
},
});
const DynamicBounds = React.createClass({
getInitialState() {
return {
min: 0,
max: 100,
};
},
onSliderChange(value) {
log(value);
},
onMinChange(e) {
this.setState({
min: +e.target.value || 0,
});
},
onMaxChange(e) {
this.setState({
max: +e.target.value || 100,
});
},
render() {
return (
<div>
<label>Min: </label>
<input type="number" value={this.state.min} onChange={this.onMinChange} />
<br />
<label>Max: </label>
<input type="number" value={this.state.max} onChange={this.onMaxChange} />
<br /><br />
<Slider defaultValue={50} min={this.state.min} max={this.state.max}
onChange={this.onSliderChange}
/>
</div>
);
},
});
ReactDOM.render(
<div>
<div style={style}>
<p>Basic Slider</p>
<Slider tipTransitionName='rc-slider-tooltip-zoom-down' onChange={log} />
<Slider tipTransitionName="rc-slider-tooltip-zoom-down" onChange={log} />
</div>
<div style={style}>
<p>Basic Slider`step=20`</p>
@@ -49,7 +93,13 @@ ReactDOM.render(
</div>
<div style={style}>
<p>Basic Slider with `tipFormatter`</p>
<Slider tipFormatter={percentFormatter} tipTransitionName='rc-slider-tooltip-zoom-down' onChange={log} />
<Slider tipFormatter={percentFormatter}
tipTransitionName="rc-slider-tooltip-zoom-down" onChange={log}
/>
</div>
<div style={style}>
<p>Basic Slider without tooltip</p>
<Slider tipFormatter={null} onChange={log} />
</div>
<div style={style}>
<p>Controlled Slider</p>
@@ -59,5 +109,9 @@ ReactDOM.render(
<p>Customized Slider</p>
<CustomizedSlider />
</div>
<div style={style}>
<p>Slider with dynamic `min` `max`</p>
<DynamicBounds />
</div>
</div>
, document.getElementById('__react-content'));
+1
View File
@@ -0,0 +1 @@
placeholder
+57
View File
@@ -0,0 +1,57 @@
require('rc-slider/assets/index.less');
const React = require('react');
const ReactDOM = require('react-dom');
const Slider = require('rc-slider');
const style = { float: 'left', width: 160, height: 400, marginBottom: 160, marginLeft: 50 };
const parentStyle = { overflow: 'hidden' };
const marks = {
'-10': '-10°C',
0: <strong>0°C</strong>,
26: '26°C',
37: '37°C',
50: '50°C',
100: {
style: {
color: 'red',
},
label: <strong>100°C</strong>,
},
};
function log(value) {
console.log(value);
}
ReactDOM.render(
<div style={parentStyle}>
<div style={style}>
<p>Slider with marks, `step=null`</p>
<Slider vertical min={-10} marks={marks} step={null} onChange={log} defaultValue={20} />
</div>
<div style={style}>
<p>Slider with marks and steps</p>
<Slider vertical dots min={-10} marks={marks} step={10} onChange={log} defaultValue={20} />
</div>
<div style={style}>
<p>Slider with marks, `included=false`</p>
<Slider vertical min={-10} marks={marks} included={false} defaultValue={20} />
</div>
<div style={style}>
<p>Slider with marks and steps, `included=false`</p>
<Slider vertical min={-10} marks={marks} step={10} included={false} defaultValue={20} />
</div>
<div style={style}>
<p>Range with marks</p>
<Slider vertical min={-10} range marks={marks} onChange={log} defaultValue={[20, 40]} />
</div>
<div style={style}>
<p>Range with marks and steps</p>
<Slider vertical min={-10} range marks={marks} step={10}
onChange={log} defaultValue={[20, 40]}
/>
</div>
</div>
, document.getElementById('__react-content'));
View File
+120
View File
@@ -0,0 +1,120 @@
/* eslint react/no-multi-comp: 0 */
require('rc-slider/assets/index.less');
const React = require('react');
const ReactDOM = require('react-dom');
const Slider = require('rc-slider');
const style = { float: 'left', width: 180, height: 400, marginBottom: 160, marginLeft: 50 };
const parentStyle = { overflow: 'hidden' };
function log(value) {
console.log(value);
}
const CustomizedRange = React.createClass({
getInitialState() {
return {
lowerBound: 20,
upperBound: 40,
value: [20, 40],
};
},
onLowerBoundChange(e) {
this.setState({ lowerBound: +e.target.value });
},
onUpperBoundChange(e) {
this.setState({ upperBound: +e.target.value });
},
onSliderChange(value) {
log(value);
this.setState({
value,
});
},
handleApply() {
const { lowerBound, upperBound } = this.state;
this.setState({ value: [lowerBound, upperBound] });
},
render() {
return (
<div style={style}>
<Slider range vertical allowCross={false} value={this.state.value}
onChange={this.onSliderChange}
/>
<label>LowerBound: </label>
<input type="number" value={this.state.lowerBound} onChange={this.onLowerBoundChange} />
<br />
<label>UpperBound: </label>
<input type="number" value={this.state.upperBound} onChange={this.onUpperBoundChange} />
<br />
<button onClick={this.handleApply}>Apply</button>
</div>
);
},
});
const DynamicBounds = React.createClass({
getInitialState() {
return {
min: 0,
max: 100,
};
},
onSliderChange(value) {
log(value);
},
onMinChange(e) {
this.setState({
min: +e.target.value || 0,
});
},
onMaxChange(e) {
this.setState({
max: +e.target.value || 100,
});
},
render() {
return (
<div style={style}>
<Slider range vertical defaultValue={[20, 50]} min={this.state.min} max={this.state.max}
onChange={this.onSliderChange}
/>
<label>Min: </label>
<input type="number" value={this.state.min} onChange={this.onMinChange} />
<br />
<label>Max: </label>
<input type="number" value={this.state.max} onChange={this.onMaxChange} />
</div>
);
},
});
ReactDOM.render(
<div style={parentStyle}>
<div style={style}>
<p>Basic Range`allowCross=false`</p>
<Slider range vertical allowCross={false} defaultValue={[0, 20]} onChange={log} />
</div>
<div style={style}>
<p>Basic Range`step=20` </p>
<Slider range vertical step={20} defaultValue={[20, 40]} onBeforeChange={log} />
</div>
<div style={style}>
<p>Basic Range`step=20, dots` </p>
<Slider range vertical dots step={20} defaultValue={[20, 40]} onAfterChange={log} />
</div>
<div style={style}>
<p>Controlled Range</p>
<Slider range vertical value={[20, 40]} />
</div>
<div style={style}>
<p>Customized Range</p>
<CustomizedRange />
</div>
<div style={style}>
<p>Range with dynamic `max` `min`</p>
<DynamicBounds />
</div>
</div>
, document.getElementById('__react-content'));
+1
View File
@@ -0,0 +1 @@
placeholder
+110
View File
@@ -0,0 +1,110 @@
/* eslint react/no-multi-comp: 0 */
require('rc-slider/assets/index.less');
const React = require('react');
const ReactDOM = require('react-dom');
const Slider = require('rc-slider');
const style = { float: 'left', width: 200, height: 400, marginBottom: 160, marginLeft: 50 };
const parentStyle = { overflow: 'hidden' };
function log(value) {
console.log(value);
}
function percentFormatter(v) {
return `${v} %`;
}
const CustomizedSlider = React.createClass({
getInitialState() {
return {
value: 50,
};
},
onSliderChange(value) {
log(value);
this.setState({
value,
});
},
render() {
return <Slider vertical value={this.state.value} onChange={this.onSliderChange} />;
},
});
const DynamicBounds = React.createClass({
getInitialState() {
return {
min: 0,
max: 100,
};
},
onSliderChange(value) {
log(value);
},
onMinChange(e) {
this.setState({
min: +e.target.value || 0,
});
},
onMaxChange(e) {
this.setState({
max: +e.target.value || 100,
});
},
render() {
return (
<div style={style}>
<p>Slider with dynamic `min` `max`</p>
<Slider vertical defaultValue={50} min={this.state.min} max={this.state.max}
onChange={this.onSliderChange}
/>
<label>Min: </label>
<input type="number" value={this.state.min} onChange={this.onMinChange} />
<br />
<label>Max: </label>
<input type="number" value={this.state.max} onChange={this.onMaxChange} />
</div>
);
},
});
ReactDOM.render(
<div style={parentStyle}>
<div style={style}>
<p>Basic Slider</p>
<Slider vertical tipTransitionName="rc-slider-tooltip-zoom-down" onChange={log} />
</div>
<div style={style}>
<p>Basic Slider`step=20`</p>
<Slider vertical step={20} defaultValue={50} onBeforeChange={log} />
</div>
<div style={style}>
<p>Basic Slider`step=20, dots`</p>
<Slider vertical dots step={20} defaultValue={100} onAfterChange={log} />
</div>
<div style={style}>
<p>Basic Slider with `tipFormatter`</p>
<Slider vertical tipFormatter={percentFormatter}
tipTransitionName="rc-slider-tooltip-zoom-down" onChange={log}
/>
</div>
<div style={style}>
<p>Basic Slider without tooltip</p>
<Slider vertical tipFormatter={null} onChange={log} />
</div>
<div style={style}>
<p>Controlled Slider</p>
<Slider vertical value={50} />
</div>
<div style={style}>
<p>Customized Slider</p>
<CustomizedSlider />
</div>
<div>
<DynamicBounds />
</div>
</div>
, document.getElementById('__react-content'));
+21 -14
View File
@@ -1,6 +1,6 @@
{
"name": "rc-slider",
"version": "3.1.2",
"version": "5.3.0",
"description": "slider ui component for react",
"keywords": [
"react",
@@ -19,39 +19,46 @@
},
"files": [
"assets/*.css",
"lib"
"lib",
"dist"
],
"licenses": "MIT",
"main": "./lib/index.js",
"entry": {
"rc-slider": ["./src/index.js", "./assets/index.less"]
},
"style": "./assets/index.css",
"config": {
"port": 8005
},
"scripts": {
"build": "rc-tools run build",
"gh-pages": "rc-tools run gh-pages",
"start": "rc-server",
"pub": "rc-tools run pub",
"start": "rc-tools run server",
"pub": "rc-tools run pub --babel-runtime",
"lint": "rc-tools run lint",
"karma": "rc-tools run karma",
"saucelabs": "rc-tools run saucelabs",
"browser-test": "rc-tools run browser-test",
"browser-test-cover": "rc-tools run browser-test-cover"
"test": "rc-tools run test",
"chrome-test": "rc-tools run chrome-test",
"coverage": "rc-tools run coverage"
},
"devDependencies": {
"expect.js": "0.3.x",
"jquery": "^1.11.2",
"pre-commit": "1.x",
"rc-server": "3.x",
"rc-tools": "4.x",
"react": "~0.14.0",
"react-addons-test-utils": "^0.14.0",
"react-dom": "~0.14.0"
"rc-tools": "^5.6.8",
"react": "^15.2.1",
"react-addons-test-utils": "^15.2.1",
"react-dom": "^15.2.1"
},
"pre-commit": [
"lint"
],
"dependencies": {
"rc-tooltip": "3.x",
"rc-util": "3.x"
"babel-runtime": "6.x",
"classnames": "^2.2.5",
"rc-tooltip": "^3.4.2",
"rc-util": "^3.2.1",
"warning": "^3.0.0"
}
}
-33
View File
@@ -1,33 +0,0 @@
import React from 'react';
import { classSet } from 'rc-util';
function calcPoints(marks, dots, step, min, max) {
const points = Object.keys(marks).map(parseFloat);
if (dots) {
for (let i = min; i <= max; i = i + step) {
points.push(i);
}
}
return points;
}
const Dots = ({prefixCls, marks, dots, step, included, lowerBound, upperBound, max, min}) => {
const range = max - min;
const elements = calcPoints(marks, dots, step, min, max).map((point) => {
const offset = (point - min) / range * 100 + '%';
const style = { left: offset };
const isActived = (!included && point === upperBound) ||
(included && point <= upperBound && point >= lowerBound);
const pointClassName = classSet({
[prefixCls + '-dot']: true,
[prefixCls + '-dot-active']: isActived,
});
return <span className={pointClassName} style={style} key={point} />;
});
return <div className={prefixCls + '-step'}>{elements}</div>;
};
export default Dots;
+35 -17
View File
@@ -23,35 +23,53 @@ export default class Handle extends React.Component {
}
render() {
const props = this.props;
const {className, tipTransitionName, tipFormatter, offset, value} = props;
const {dragging, noTip} = props;
const {
prefixCls,
tooltipPrefixCls,
className,
tipTransitionName,
tipFormatter,
vertical,
offset,
value,
dragging,
noTip,
} = this.props;
const style = { left: offset + '%' };
const handle = (<div className={className} style={style}
onMouseUp={this.showTooltip.bind(this)}
onMouseEnter={this.showTooltip.bind(this)}
onMouseLeave={this.hideTooltip.bind(this)}/>);
const style = vertical ? { bottom: `${offset}%` } : { left: `${offset}%` };
const handle = (
<div className={className} style={style}
onMouseUp={this.showTooltip.bind(this)}
onMouseEnter={this.showTooltip.bind(this)}
onMouseLeave={this.hideTooltip.bind(this)}
/>
);
if (noTip) {
return handle;
}
const isTooltipVisible = dragging || this.state.isTooltipVisible;
return (<Tooltip
prefixCls={className.replace('slider-handle', 'tooltip')}
placement="top"
visible={isTooltipVisible}
overlay={<span>{tipFormatter ? tipFormatter(value) : value}</span>}
delay={0}
transitionName={tipTransitionName}>
{handle}
</Tooltip>);
return (
<Tooltip
prefixCls={tooltipPrefixCls || `${prefixCls}-tooltip`}
placement="top"
visible={isTooltipVisible}
overlay={<span>{tipFormatter(value)}</span>}
delay={0}
transitionName={tipTransitionName}
>
{handle}
</Tooltip>
);
}
}
Handle.propTypes = {
prefixCls: React.PropTypes.string,
tooltipPrefixCls: React.PropTypes.string,
className: React.PropTypes.string,
vertical: React.PropTypes.bool,
offset: React.PropTypes.number,
tipTransitionName: React.PropTypes.string,
tipFormatter: React.PropTypes.func,
+27 -10
View File
@@ -1,26 +1,43 @@
import React from 'react';
import rcUtil from 'rc-util';
import classNames from 'classnames';
const Marks = ({className, marks, included, upperBound, lowerBound, max, min}) => {
const Marks = ({ className, vertical, marks, included, upperBound, lowerBound, max, min }) => {
const marksKeys = Object.keys(marks);
const marksCount = marksKeys.length;
const unit = 100 / (marksCount - 1);
const markWidth = unit * 0.9;
const range = max - min;
const elements = marksKeys.map(parseFloat).map((point) => {
const elements = marksKeys.map(parseFloat).sort((a, b) => a - b).map((point) => {
const isActived = (!included && point === upperBound) ||
(included && point <= upperBound && point >= lowerBound);
const markClassName = rcUtil.classSet({
[className + '-text']: true,
[className + '-text-active']: isActived,
const markClassName = classNames({
[`${className}-text`]: true,
[`${className}-text-active`]: isActived,
});
const style = { width: markWidth + '%' };
style.left = (point - min) / range * 100 - markWidth / 2 + '%';
const bottomStyle = {
// height: markWidth + '%',
marginBottom: '-50%',
bottom: `${(point - min) / range * 100}%`,
};
return (<span className={markClassName} style={style} key={point}>
{marks[point]}
const leftStyle = {
width: `${markWidth}%`,
marginLeft: `${-markWidth / 2}%`,
left: `${(point - min) / range * 100}%`,
};
const style = vertical ? bottomStyle : leftStyle;
const markPoint = marks[point];
const markPointIsObject = typeof markPoint === 'object' &&
!React.isValidElement(markPoint);
const markLabel = markPointIsObject ? markPoint.label : markPoint;
const markStyle = markPointIsObject ?
{ ...style, ...markPoint.style } : style;
return (<span className={markClassName} style={markStyle} key={point}>
{markLabel}
</span>);
});
+339 -125
View File
@@ -1,9 +1,12 @@
import React from 'react';
import {Dom as DomUtils, classSet} from 'rc-util';
import { findDOMNode } from 'react-dom';
import React, { cloneElement } from 'react';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import classNames from 'classnames';
import Track from './Track';
import Handle from './Handle';
import Dots from './Dots';
import DefaultHandle from './Handle';
import Steps from './Steps';
import Marks from './Marks';
import warning from 'warning';
function noop() {
}
@@ -12,12 +15,19 @@ function isNotTouchEvent(e) {
return e.touches.length > 1 || (e.type.toLowerCase() === 'touchend' && e.touches.length > 0);
}
function getTouchPosition(e) {
return e.touches[0].pageX;
function getTouchPosition(vertical, e) {
return vertical ? e.touches[0].clientY : e.touches[0].pageX;
}
function getMousePosition(e) {
return e.pageX;
function getMousePosition(vertical, e) {
return vertical ? e.clientY : e.pageX;
}
function getHandleCenterPosition(vertical, handle) {
const coords = handle.getBoundingClientRect();
return vertical ?
coords.top + (coords.height * 0.5) :
coords.left + (coords.width * 0.5);
}
function pauseEvent(e) {
@@ -29,77 +39,80 @@ class Slider extends React.Component {
constructor(props) {
super(props);
const {range, min, max} = props;
const initialValue = range ? [min, min] : min;
const { range, min, max, step } = props;
const initialValue = range ? Array.apply(null, Array(range + 1)).map(() => min) : min;
const defaultValue = ('defaultValue' in props ? props.defaultValue : initialValue);
const value = ('value' in props ? props.value : defaultValue);
const value = (props.value !== undefined ? props.value : defaultValue);
let upperBound;
let lowerBound;
if (props.range) {
lowerBound = this.trimAlignValue(value[0]);
upperBound = this.trimAlignValue(value[1]);
} else {
upperBound = this.trimAlignValue(value);
}
const bounds = (range ? value : [min, value]).map(v => this.trimAlignValue(v));
let recent;
if (props.range && upperBound === lowerBound) {
if (lowerBound === max) {
recent = 'lowerBound';
}
if (upperBound === min) {
recent = 'upperBound';
}
if (range && bounds[0] === bounds[bounds.length - 1] && bounds[0] === max) {
recent = 0;
} else {
recent = 'upperBound';
recent = bounds.length - 1;
}
if (process.env.NODE_ENV !== 'production' &&
step && Math.floor(step) === step &&
(max - min) % step !== 0) {
warning(
false,
'Slider[max] - Slider[min] (%s) should be a multiple of Slider[step] (%s)',
max - min,
step
);
}
this.state = {
handle: null,
recent: recent,
upperBound: upperBound,
// If Slider is not range, set `lowerBound` equal to `min`.
lowerBound: (lowerBound || min),
recent,
bounds,
};
}
componentWillReceiveProps(nextProps) {
if (!('value' in nextProps || 'min' in nextProps || 'max' in nextProps)) return;
const { bounds } = this.state;
if (nextProps.range) {
const value = nextProps.value;
if (value) {
this.setState({
upperBound: value[1],
lowerBound: value[0],
});
const value = nextProps.value || bounds;
const nextBounds = value.map(v => this.trimAlignValue(v, nextProps));
if (nextBounds.every((v, i) => v === bounds[i])) return;
this.setState({ bounds: nextBounds });
if (bounds.some(v => this.isValueOutOfBounds(v, nextProps))) {
this.props.onChange(nextBounds);
}
} else {
const value = nextProps.value !== undefined ? nextProps.value : bounds[1];
const nextValue = this.trimAlignValue(value, nextProps);
if (nextValue === bounds[1] && bounds[0] === nextProps.min) return;
this.setState({ bounds: [nextProps.min, nextValue] });
if (this.isValueOutOfBounds(bounds[1], nextProps)) {
this.props.onChange(nextValue);
}
} else if ('value' in nextProps) {
this.setState({
upperBound: nextProps.value,
});
}
}
onChange(handle, value) {
onChange(state) {
const props = this.props;
const isNotControlled = !('value' in props);
if (isNotControlled) {
this.setState({[handle]: value});
this.setState(state);
} else if (state.handle !== undefined) {
this.setState({ handle: state.handle });
}
const state = this.state;
const data = {
upperBound: state.upperBound,
lowerBound: state.lowerBound,
};
data[handle] = value;
const changedValue = props.range ? [data.lowerBound, data.upperBound] : data.upperBound;
const data = { ...this.state, ...state };
const changedValue = props.range ? data.bounds : data.bounds[1];
props.onChange(changedValue);
}
onMouseMove(e) {
const position = getMousePosition(e);
this.onMove(e, position);
const position = getMousePosition(this.props.vertical, e);
this.onMove(e, position - this.dragOffset);
}
onTouchMove(e) {
@@ -108,8 +121,8 @@ class Slider extends React.Component {
return;
}
const position = getTouchPosition(e);
this.onMove(e, position);
const position = getTouchPosition(this.props.vertical, e);
this.onMove(e, position - this.dragOffset);
}
onMove(e, position) {
@@ -117,27 +130,57 @@ class Slider extends React.Component {
const props = this.props;
const state = this.state;
const diffPosition = position - this.startPosition;
let diffPosition = position - this.startPosition;
diffPosition = this.props.vertical ? -diffPosition : diffPosition;
const diffValue = diffPosition / this.getSliderLength() * (props.max - props.min);
const value = this.trimAlignValue(this.startValue + diffValue);
const oldValue = state[state.handle];
const oldValue = state.bounds[state.handle];
if (value === oldValue) return;
this.onChange(state.handle, value);
const nextBounds = [...state.bounds];
nextBounds[state.handle] = value;
let nextHandle = state.handle;
if (props.pushable !== false) {
const originalValue = state.bounds[nextHandle];
this.pushSurroundingHandles(nextBounds, nextHandle, originalValue);
} else if (props.allowCross) {
nextBounds.sort((a, b) => a - b);
nextHandle = nextBounds.indexOf(value);
}
this.onChange({
handle: nextHandle,
bounds: nextBounds,
});
}
onTouchStart(e) {
if (isNotTouchEvent(e)) return;
const position = getTouchPosition(e);
let position = getTouchPosition(this.props.vertical, e);
if (!this.isEventFromHandle(e)) {
this.dragOffset = 0;
} else {
const handlePosition = getHandleCenterPosition(this.props.vertical, e.target);
this.dragOffset = position - handlePosition;
position = handlePosition;
}
this.onStart(position);
this.addDocumentEvents('touch');
pauseEvent(e);
}
onMouseDown(e) {
const position = getMousePosition(e);
if (e.button !== 0) { return; }
let position = getMousePosition(this.props.vertical, e);
if (!this.isEventFromHandle(e)) {
this.dragOffset = 0;
} else {
const handlePosition = getHandleCenterPosition(this.props.vertical, e.target);
this.dragOffset = position - handlePosition;
position = handlePosition;
}
this.onStart(position);
this.addDocumentEvents('mouse');
pauseEvent(e);
@@ -152,22 +195,26 @@ class Slider extends React.Component {
this.startPosition = position;
const state = this.state;
const {upperBound, lowerBound} = state;
const { bounds } = state;
let valueNeedChanging = 'upperBound';
let valueNeedChanging = 1;
if (this.props.range) {
const isLowerBoundCloser = Math.abs(upperBound - value) > Math.abs(lowerBound - value);
if (isLowerBoundCloser) {
valueNeedChanging = 'lowerBound';
let closestBound = 0;
for (let 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;
}
valueNeedChanging = closestBound;
const isAtTheSamePoint = (upperBound === lowerBound);
const isAtTheSamePoint = (bounds[closestBound + 1] === bounds[closestBound]);
if (isAtTheSamePoint) {
valueNeedChanging = state.recent;
}
if (isAtTheSamePoint && (value !== upperBound)) {
valueNeedChanging = value < upperBound ? 'lowerBound' : 'upperBound';
if (isAtTheSamePoint && (value !== bounds[closestBound + 1])) {
valueNeedChanging = value < bounds[closestBound + 1] ? closestBound : closestBound + 1;
}
}
@@ -176,15 +223,17 @@ class Slider extends React.Component {
recent: valueNeedChanging,
});
const oldValue = state[valueNeedChanging];
const oldValue = state.bounds[valueNeedChanging];
if (value === oldValue) return;
this.onChange(valueNeedChanging, value);
const nextBounds = [...state.bounds];
nextBounds[valueNeedChanging] = value;
this.onChange({ bounds: nextBounds });
}
getValue() {
const {lowerBound, upperBound} = this.state;
return this.props.range ? [lowerBound, upperBound] : upperBound;
const { bounds } = this.state;
return this.props.range ? bounds : bounds[1];
}
getSliderLength() {
@@ -193,19 +242,18 @@ class Slider extends React.Component {
return 0;
}
return slider.clientWidth;
return this.props.vertical ? slider.clientHeight : slider.clientWidth;
}
getSliderStart() {
const slider = this.refs.slider;
const rect = slider.getBoundingClientRect();
return rect.left;
return this.props.vertical ? rect.top : rect.left;
}
getPrecision() {
const props = this.props;
const stepString = props.step.toString();
getPrecision(step) {
const stepString = step.toString();
let precision = 0;
if (stepString.indexOf('.') >= 0) {
precision = stepString.length - stepString.indexOf('.') - 1;
@@ -213,10 +261,42 @@ class Slider extends React.Component {
return precision;
}
trimAlignValue(v) {
/**
* Returns an array of possible slider points, taking into account both
* `marks` and `step`. The result is cached.
*/
getPoints() {
const { marks, step, min, max } = this.props;
const cache = this._getPointsCache;
if (!cache || cache.marks !== marks || cache.step !== step) {
const pointsObject = { ...marks };
if (step !== null) {
for (let point = min; point <= max; point += step) {
pointsObject[point] = point;
}
}
const points = Object.keys(pointsObject).map(parseFloat);
points.sort((a, b) => a - b);
this._getPointsCache = { marks, step, points };
}
return this._getPointsCache.points;
}
isEventFromHandle(e) {
return this.state.bounds.some((x, i) => (
this.refs[`handle-${i}`] &&
e.target === findDOMNode(this.refs[`handle-${i}`])
));
}
isValueOutOfBounds(value, props) {
return value < props.min || value > props.max;
}
trimAlignValue(v, nextProps) {
const state = this.state || {};
const {handle, lowerBound, upperBound} = state;
const {marks, step, min, max} = this.props;
const { handle, bounds } = state;
const { marks, step, min, max, allowCross } = { ...this.props, ...(nextProps || {}) };
let val = v;
if (val <= min) {
@@ -225,35 +305,96 @@ class Slider extends React.Component {
if (val >= max) {
val = max;
}
if (handle === 'upperBound' && val <= lowerBound) {
val = lowerBound;
/* eslint-disable eqeqeq */
if (!allowCross && handle != null && handle > 0 && val <= bounds[handle - 1]) {
val = bounds[handle - 1];
}
if (handle === 'lowerBound' && val >= upperBound) {
val = upperBound;
if (!allowCross && handle != null && handle < bounds.length - 1 && val >= bounds[handle + 1]) {
val = bounds[handle + 1];
}
/* eslint-enable eqeqeq */
const points = Object.keys(marks).map(parseFloat);
if (step !== null) {
const closestStep = Math.round(val / step) * step;
const closestStep = (Math.round((val - min) / step) * step) + min;
points.push(closestStep);
}
const diffs = points.map((point) => Math.abs(val - point));
const closestPoint = points[diffs.indexOf(Math.min.apply(Math, diffs))];
return step !== null ? parseFloat(closestPoint.toFixed(this.getPrecision())) : closestPoint;
return step !== null ? parseFloat(closestPoint.toFixed(this.getPrecision(step))) : closestPoint;
}
pushHandleOnePoint(bounds, handle, direction) {
const points = this.getPoints();
const pointIndex = points.indexOf(bounds[handle]);
const nextPointIndex = pointIndex + direction;
if (nextPointIndex >= points.length || nextPointIndex < 0) {
// reached the minimum or maximum available point, can't push anymore
return false;
}
const nextHandle = handle + direction;
const nextValue = points[nextPointIndex];
const { pushable: threshold } = this.props;
const 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;
}
pushHandle(bounds, handle, direction, amount) {
const originalValue = bounds[handle];
let 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;
}
pushSurroundingHandles(bounds, handle, originalValue) {
const { pushable: threshold } = this.props;
const value = bounds[handle];
let direction = 0;
if (bounds[handle + 1] - value < threshold) {
direction = +1;
} else if (value - bounds[handle - 1] < threshold) {
direction = -1;
}
if (direction === 0) { return; }
const nextHandle = handle + direction;
const diffToNext = direction * (bounds[nextHandle] - value);
if (!this.pushHandle(bounds, nextHandle, direction, threshold - diffToNext)) {
// revert to original value if pushing is impossible
bounds[handle] = originalValue;
}
}
calcOffset(value) {
const {min, max} = this.props;
const { min, max } = this.props;
const ratio = (value - min) / (max - min);
return ratio * 100;
}
calcValue(offset) {
const {min, max} = this.props;
const ratio = offset / this.getSliderLength();
return ratio * (max - min) + min;
const { vertical, min, max } = this.props;
const ratio = Math.abs(offset / this.getSliderLength());
const value = vertical ? (1 - ratio) * (max - min) + min : ratio * (max - min) + min;
return value;
}
calcValueByPos(position) {
@@ -265,11 +406,15 @@ class Slider extends React.Component {
addDocumentEvents(type) {
if (type === 'touch') {
// just work for chrome iOS Safari and Android Browser
this.onTouchMoveListener = DomUtils.addEventListener(document, 'touchmove', this.onTouchMove.bind(this));
this.onTouchUpListener = DomUtils.addEventListener(document, 'touchend', this.end.bind(this, 'touch'));
this.onTouchMoveListener =
addEventListener(document, 'touchmove', this.onTouchMove.bind(this));
this.onTouchUpListener =
addEventListener(document, 'touchend', this.end.bind(this, 'touch'));
} else if (type === 'mouse') {
this.onMouseMoveListener = DomUtils.addEventListener(document, 'mousemove', this.onMouseMove.bind(this));
this.onMouseUpListener = DomUtils.addEventListener(document, 'mouseup', this.end.bind(this, 'mouse'));
this.onMouseMoveListener =
addEventListener(document, 'mousemove', this.onMouseMove.bind(this));
this.onMouseUpListener =
addEventListener(document, 'mouseup', this.end.bind(this, 'mouse'));
}
}
@@ -286,51 +431,104 @@ class Slider extends React.Component {
end(type) {
this.removeEvents(type);
this.props.onAfterChange(this.getValue());
this.setState({handle: null});
this.setState({ handle: null });
}
render() {
const {handle, upperBound, lowerBound} = this.state;
const {className, prefixCls, disabled, dots, included, range, step,
marks, max, min, tipTransitionName, tipFormatter, children} = this.props;
const {
handle,
bounds,
} = this.state;
const {
className,
prefixCls,
tooltipPrefixCls,
disabled,
vertical,
dots,
included,
range,
step,
marks,
max, min,
tipTransitionName,
tipFormatter,
children,
} = this.props;
const upperOffset = this.calcOffset(upperBound);
const lowerOffset = this.calcOffset(lowerBound);
const customHandle = this.props.handle;
const handleClassName = prefixCls + '-handle';
const isNoTip = (step === null) && !tipFormatter;
const offsets = bounds.map(v => this.calcOffset(v));
const upper = (<Handle className={handleClassName}
noTip={isNoTip} tipTransitionName={tipTransitionName} tipFormatter={tipFormatter}
offset={upperOffset} value={upperBound} dragging={handle === 'upperBound'} />);
const handleClassName = `${prefixCls}-handle`;
let lower = null;
if (range) {
lower = (<Handle className={handleClassName}
noTip={isNoTip} tipTransitionName={tipTransitionName} tipFormatter={tipFormatter}
offset={lowerOffset} value={lowerBound} dragging={handle === 'lowerBound'} />);
const handlesClassNames = bounds.map((v, i) => classNames({
[handleClassName]: true,
[`${handleClassName}-${i + 1}`]: true,
[`${handleClassName}-lower`]: i === 0,
[`${handleClassName}-upper`]: i === bounds.length - 1,
}));
const isNoTip = (step === null) || (tipFormatter === null);
const commonHandleProps = {
prefixCls,
tooltipPrefixCls,
noTip: isNoTip,
tipTransitionName,
tipFormatter,
vertical,
};
const handles = bounds.map((v, i) => cloneElement(customHandle, {
...commonHandleProps,
className: handlesClassNames[i],
value: v,
offset: offsets[i],
dragging: handle === i,
key: i,
ref: `handle-${i}`,
}));
if (!range) { handles.shift(); }
const isIncluded = included || range;
const tracks = [];
for (let i = 1; i < bounds.length; ++i) {
const trackClassName = classNames({
[`${prefixCls}-track`]: true,
[`${prefixCls}-track-${i}`]: true,
});
tracks.push(
<Track className={trackClassName} vertical={vertical} included={isIncluded}
offset={offsets[i - 1]} length={offsets[i] - offsets[i - 1]} key={i}
/>
);
}
const sliderClassName = classSet({
const sliderClassName = classNames({
[prefixCls]: true,
[prefixCls + '-disabled']: disabled,
[`${prefixCls}-disabled`]: disabled,
[className]: !!className,
[`${prefixCls}-vertical`]: this.props.vertical,
});
const isIncluded = included || range;
return (
<div ref="slider" className={sliderClassName}
onTouchStart={disabled ? noop : this.onTouchStart.bind(this)}
onMouseDown={disabled ? noop : this.onMouseDown.bind(this)}>
{upper}
{lower}
<Track className={prefixCls + '-track'} included={isIncluded}
offset={lowerOffset} length={upperOffset - lowerOffset}/>
<Dots prefixCls={prefixCls} marks={marks} dots={dots} step={step}
included={isIncluded} lowerBound={lowerBound}
upperBound={upperBound} max={max} min={min} />
<Marks className={prefixCls + '-mark'} marks={marks}
included={isIncluded} lowerBound={lowerBound}
upperBound={upperBound} max={max} min={min} />
onTouchStart={disabled ? noop : this.onTouchStart.bind(this)}
onMouseDown={disabled ? noop : this.onMouseDown.bind(this)}
>
<div className={`${prefixCls}-rail`} />
{tracks}
<Steps prefixCls={prefixCls} vertical = {vertical} marks={marks} dots={dots} step={step}
included={isIncluded} lowerBound={bounds[0]}
upperBound={bounds[bounds.length - 1]} max={max} min={min}
/>
{handles}
<Marks className={`${prefixCls}-mark`} vertical = {vertical} marks={marks}
included={isIncluded} lowerBound={bounds[0]}
upperBound={bounds[bounds.length - 1]} max={max} min={min}
/>
{children}
</div>
);
@@ -353,15 +551,26 @@ Slider.propTypes = {
included: React.PropTypes.bool,
className: React.PropTypes.string,
prefixCls: React.PropTypes.string,
tooltipPrefixCls: React.PropTypes.string,
disabled: React.PropTypes.bool,
children: React.PropTypes.any,
onBeforeChange: React.PropTypes.func,
onChange: React.PropTypes.func,
onAfterChange: React.PropTypes.func,
handle: React.PropTypes.element,
tipTransitionName: React.PropTypes.string,
tipFormatter: React.PropTypes.func,
dots: React.PropTypes.bool,
range: React.PropTypes.bool,
range: React.PropTypes.oneOfType([
React.PropTypes.bool,
React.PropTypes.number,
]),
vertical: React.PropTypes.bool,
allowCross: React.PropTypes.bool,
pushable: React.PropTypes.oneOfType([
React.PropTypes.bool,
React.PropTypes.number,
]),
};
Slider.defaultProps = {
@@ -372,13 +581,18 @@ Slider.defaultProps = {
max: 100,
step: 1,
marks: {},
handle: <DefaultHandle />,
onBeforeChange: noop,
onChange: noop,
onAfterChange: noop,
tipFormatter: value => value,
included: true,
disabled: false,
dots: false,
range: false,
vertical: false,
allowCross: true,
pushable: false,
};
export default Slider;
+40
View File
@@ -0,0 +1,40 @@
import React from 'react';
import classNames from 'classnames';
import warning from 'warning';
function calcPoints(vertical, marks, dots, step, min, max) {
warning(
dots ? step > 0 : true,
'`Slider[step]` should be a positive number in order to make Slider[dots] work.'
);
const points = Object.keys(marks).map(parseFloat);
if (dots) {
for (let i = min; i <= max; i = i + step) {
if (points.indexOf(i) >= 0) continue;
points.push(i);
}
}
return points;
}
const Steps = ({ prefixCls, vertical, marks, dots, step, included,
lowerBound, upperBound, max, min }) => {
const range = max - min;
const elements = calcPoints(vertical, marks, dots, step, min, max).map((point) => {
const offset = `${Math.abs(point - min) / range * 100}%`;
const style = vertical ? { bottom: offset } : { left: offset };
const isActived = (!included && point === upperBound) ||
(included && point <= upperBound && point >= lowerBound);
const pointClassName = classNames({
[`${prefixCls}-dot`]: true,
[`${prefixCls}-dot-active`]: isActived,
});
return <span className={pointClassName} style={style} key={point} />;
});
return <div className={`${prefixCls}-step`}>{elements}</div>;
};
export default Steps;
+8 -3
View File
@@ -1,11 +1,16 @@
import React from 'react';
const Track = ({className, included, offset, length}) => {
const Track = ({ className, included, vertical, offset, length }) => {
const style = {
left: offset + '%',
width: length + '%',
visibility: included ? 'visible' : 'hidden',
};
if (vertical) {
style.bottom = `${offset}%`;
style.height = `${length}%`;
} else {
style.left = `${offset}%`;
style.width = `${length}%`;
}
return <div className={className} style={style} />;
};
+343
View File
@@ -0,0 +1,343 @@
/* eslint-disable max-len */
const expect = require('expect.js');
const React = require('react');
const ReactDOM = require('react-dom');
const ReactTestUtils = require('react-addons-test-utils');
const Slider = require('..');
function createSliderWrapperComponent() {
return class SliderWrapper extends React.Component {
render() {
return (
<div style={{ position: `absolute`, width: `100px`, height: `10px` }}>
<Slider ref="slider"/>
</div>
);
}
};
}
require('../assets/index.less');
describe('rc-slider', function test() {
this.timeout(5000);
const div = document.createElement('div');
document.body.appendChild(div);
afterEach(() => {
ReactDOM.unmountComponentAtNode(div);
});
it('should render a Slider with correct DOM structure', () => {
const slider = ReactDOM.render(<Slider />, div);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-handle').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-track').length).to.be(1);
});
it('should render a Slider with default value correctly', () => {
const sliderWithDefaultValue = ReactDOM.render(<Slider defaultValue={50} />, div);
expect(sliderWithDefaultValue.state.bounds[1]).to.be(50);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(sliderWithDefaultValue, 'rc-slider-handle')[0]
.style.cssText)
.to.match(/left: 50%;/);
const trackStyle = ReactTestUtils
.scryRenderedDOMComponentsWithClass(sliderWithDefaultValue, 'rc-slider-track')[0]
.style.cssText;
expect(trackStyle).to.match(/left: 0%;/);
expect(trackStyle).to.match(/width: 50%;/);
expect(trackStyle).to.match(/visibility: visible;/);
});
it('should render a Slider with value corrently', () => {
const sliderWithValue = ReactDOM.render(<Slider value={50} />, div);
expect(sliderWithValue.state.bounds[1]).to.be(50);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(sliderWithValue, 'rc-slider-handle')[0]
.style.cssText)
.to.match(/left: 50%;/);
const trackStyle = ReactTestUtils
.scryRenderedDOMComponentsWithClass(sliderWithValue, 'rc-slider-track')[0]
.style.cssText;
expect(trackStyle).to.match(/left: 0%;/);
expect(trackStyle).to.match(/width: 50%;/);
expect(trackStyle).to.match(/visibility: visible;/);
});
it('should render a Range with correct DOM structure', () => {
const range = ReactDOM.render(<Slider range />, div);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(range, 'rc-slider').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(range, 'rc-slider-handle').length).to.be(2);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(range, 'rc-slider-track').length).to.be(1);
});
it('should render a Multi-Range with correct DOM structure', () => {
const multiRange = ReactDOM.render(<Slider range={3} />, div);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider-handle').length).to.be(4);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider-handle-1').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider-handle-2').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider-handle-3').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider-handle-4').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider-track').length).to.be(3);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider-track-1').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider-track-2').length).to.be(1);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(multiRange, 'rc-slider-track-3').length).to.be(1);
});
it('should render a Range with default value correctly', () => {
const rangeWithDefaultValue = ReactDOM.render(<Slider range defaultValue={[0, 50]} />, div);
expect(rangeWithDefaultValue.state.bounds[0]).to.be(0);
expect(rangeWithDefaultValue.state.bounds[1]).to.be(50);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithDefaultValue, 'rc-slider-handle')[0]
.style.cssText)
.to.match(/left: 0%;/);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithDefaultValue, 'rc-slider-handle')[1]
.style.cssText)
.to.match(/left: 50%;/);
const trackStyle = ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithDefaultValue, 'rc-slider-track')[0]
.style.cssText;
expect(trackStyle).to.match(/left: 0%;/);
expect(trackStyle).to.match(/width: 50%;/);
expect(trackStyle).to.match(/visibility: visible;/);
});
it('should render a Multi-Range with default value correctly', () => {
const multiRangeWithDefaultValue = ReactDOM.render(<Slider range={3} defaultValue={[0, 25, 50, 75]} />, div);
expect(multiRangeWithDefaultValue.state.bounds[0]).to.be(0);
expect(multiRangeWithDefaultValue.state.bounds[1]).to.be(25);
expect(multiRangeWithDefaultValue.state.bounds[2]).to.be(50);
expect(multiRangeWithDefaultValue.state.bounds[3]).to.be(75);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(multiRangeWithDefaultValue, 'rc-slider-handle')[0]
.style.cssText)
.to.match(/left: 0%;/);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(multiRangeWithDefaultValue, 'rc-slider-handle')[1]
.style.cssText)
.to.match(/left: 25%;/);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(multiRangeWithDefaultValue, 'rc-slider-handle')[2]
.style.cssText)
.to.match(/left: 50%;/);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(multiRangeWithDefaultValue, 'rc-slider-handle')[3]
.style.cssText)
.to.match(/left: 75%;/);
const track1Style = ReactTestUtils
.scryRenderedDOMComponentsWithClass(multiRangeWithDefaultValue, 'rc-slider-track-1')[0]
.style.cssText;
expect(track1Style).to.match(/left: 0%;/);
expect(track1Style).to.match(/width: 25%;/);
expect(track1Style).to.match(/visibility: visible;/);
const track2Style = ReactTestUtils
.scryRenderedDOMComponentsWithClass(multiRangeWithDefaultValue, 'rc-slider-track-2')[0]
.style.cssText;
expect(track2Style).to.match(/left: 25%;/);
expect(track2Style).to.match(/width: 25%;/);
expect(track2Style).to.match(/visibility: visible;/);
const track3Style = ReactTestUtils
.scryRenderedDOMComponentsWithClass(multiRangeWithDefaultValue, 'rc-slider-track-3')[0]
.style.cssText;
expect(track3Style).to.match(/left: 50%;/);
expect(track3Style).to.match(/width: 25%;/);
expect(track3Style).to.match(/visibility: visible;/);
});
it('should render a Range with value correctly', () => {
const rangeWithValue = ReactDOM.render(<Slider range value={[50, 100]} />, div);
expect(rangeWithValue.state.bounds[0]).to.be(50);
expect(rangeWithValue.state.bounds[1]).to.be(100);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithValue, 'rc-slider-handle')[0]
.style.cssText)
.to.match(/left: 50%;/);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithValue, 'rc-slider-handle')[1]
.style.cssText)
.to.match(/left: 100%;/);
const trackStyle = ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithValue, 'rc-slider-track')[0]
.style.cssText;
expect(trackStyle).to.match(/left: 50%;/);
expect(trackStyle).to.match(/width: 50%;/);
expect(trackStyle).to.match(/visibility: visible;/);
});
it('should render dots correctly when `dots=true`', () => {
const slider = ReactDOM.render(<Slider value={50} step={10} dots />, div);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-dot').length).to.be(11);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-dot-active').length).to.be(6);
const range = ReactDOM.render(<Slider range value={[20, 50]} step={10} dots />, div);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(range, 'rc-slider-dot').length).to.be(11);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(range, 'rc-slider-dot-active').length).to.be(4);
});
it('should render marks correctly when `marks` is not an empty object', () => {
const marks = { 0: '0', 30: '30', 100: '100' };
const slider = ReactDOM.render(<Slider value={30} marks={marks} />, div);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-mark-text').length).to.be(3);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-mark-text')[0].innerHTML).to.be('0');
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-mark-text')[1].innerHTML).to.be('30');
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-mark-text')[2].innerHTML).to.be('100');
const range = ReactDOM.render(<Slider range value={[0, 30]} marks={marks} />, div);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(range, 'rc-slider-mark-text').length).to.be(3);
});
it('should not set value greater than `max` or smaller `min`', () => {
const sliderWithMin = ReactDOM.render(<Slider value={0} min={10} />, div);
expect(sliderWithMin.state.bounds[1]).to.be(10);
ReactDOM.unmountComponentAtNode(div);
const sliderWithMax = ReactDOM.render(<Slider value={100} max={90} />, div);
expect(sliderWithMax.state.bounds[1]).to.be(90);
ReactDOM.unmountComponentAtNode(div);
const range = ReactDOM.render(<Slider range value={[0, 100]} min={10} max={90} />, div);
expect(range.state.bounds[0]).to.be(10);
expect(range.state.bounds[1]).to.be(90);
ReactDOM.unmountComponentAtNode(div);
});
it('should render a vertical slider, when `vertical` is true', () => {
const slider = ReactDOM.render(<Slider vertical />, div);
expect(ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-vertical').length).to.be(1);
});
it('should not call onChange when value is the same', () => {
const values = [];
const handler = (e) => {
values.push(e);
};
ReactDOM.render(<Slider onChange={handler}/>, div);
const handle = div.querySelector('.rc-slider-handle');
const down = document.createEvent('MouseEvent');
down.initEvent('mousedown', true, true);
const move = document.createEvent('MouseEvent');
move.initEvent('mousemove', true, true);
const up = document.createEvent('MouseEvent');
up.initEvent('mouseup', true, true);
handle.dispatchEvent(down);
handle.dispatchEvent(move);
handle.dispatchEvent(up);
expect(values.length).to.be(0);
});
it('should set `dragOffset` to correct value when the left handle is clicked off-center', () => {
const slider = ReactDOM.render(<Slider />, div);
const leftHandle = ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-handle')[0];
slider.onMouseDown({
type: 'mousedown',
target: leftHandle,
pageX: 5, button: 0,
stopPropagation() {},
preventDefault() {},
});
expect(slider.dragOffset).to.be(5);
});
it('should respect `dragOffset` while dragging the handle via MouseEvents', () => {
const SliderWrapper = createSliderWrapperComponent();
const slider = ReactDOM.render(<SliderWrapper/>, div).refs.slider;
const leftHandle = ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-handle')[0];
slider.onMouseDown({
type: 'mousedown',
target: leftHandle,
pageX: 5, button: 0,
stopPropagation() {},
preventDefault() {},
});
expect(slider.dragOffset).to.be(5);
slider.onMouseMove({
type: 'mousemove',
target: leftHandle,
pageX: 14, button: 0,
stopPropagation() {},
preventDefault() {},
});
expect(slider.getValue()).to.be(9);
});
it('should set `dragOffset` to 0 when the MouseEvent target isn\'t a handle', () => {
const slider = ReactDOM.render(<Slider />, div);
const sliderTrack = ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-track')[0];
slider.onMouseDown({
type: 'mousedown',
target: sliderTrack,
pageX: 5, button: 0,
stopPropagation() {},
preventDefault() {},
});
expect(slider.dragOffset).to.be(0);
});
it('should set `dragOffset` to correct value when the left handle is touched off-center', () => {
const slider = ReactDOM.render(<Slider />, div);
const leftHandle = ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-handle')[0];
slider.onTouchStart({
type: 'touchstart',
target: leftHandle,
touches: [{ pageX: 5 }],
stopPropagation() {},
preventDefault() {},
});
expect(slider.dragOffset).to.be(5);
});
it('should respect `dragOffset` while dragging the handle via TouchEvents', () => {
const SliderWrapper = createSliderWrapperComponent();
const slider = ReactDOM.render(<SliderWrapper/>, div).refs.slider;
const leftHandle = ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-handle')[0];
slider.onTouchStart({
type: 'touchstart',
target: leftHandle,
touches: [{ pageX: 5 }],
stopPropagation() {},
preventDefault() {},
});
expect(slider.dragOffset).to.be(5);
slider.onTouchMove({
type: 'touchmove',
target: leftHandle,
touches: [{ pageX: 14 }],
stopPropagation() {},
preventDefault() {},
});
expect(slider.getValue()).to.be(9);
});
it('should set `dragOffset` to 0 when the TouchEvent target isn\'t a handle', () => {
const slider = ReactDOM.render(<Slider />, div);
const sliderTrack = ReactTestUtils.scryRenderedDOMComponentsWithClass(slider, 'rc-slider-track')[0];
slider.onTouchStart({
type: 'touchstart',
target: sliderTrack,
touches: [{ pageX: 5 }],
stopPropagation() {},
preventDefault() {},
});
expect(slider.dragOffset).to.be(0);
});
});
-83
View File
@@ -1,83 +0,0 @@
var expect = require('expect.js');
var Slider = require('../index.js');
var React = require('react');
var ReactDOM = require('react-dom');
var TestUtils = require('react-addons-test-utils');
var Simulate = TestUtils.Simulate;
var $ = require('jquery');
require('../assets/index.less');
describe('rc-slider', function () {
this.timeout(5000);
var div = document.createElement('div');
document.body.appendChild(div);
afterEach(function () {
ReactDOM.unmountComponentAtNode(div);
});
it('should render a simple slider with value correctly!', function () {
var slider = ReactDOM.render(
<Slider className='rc-slider' defaultValue={40}/>,
div
);
var node = $(div);
expect(node.find('.rc-slider').length).to.be(1);
expect(node.find('.rc-slider-handle').length).to.be(1);
expect(node.find('.rc-slider-track').length).to.be(1);
expect(slider.state.upperBound).to.be(40);
var trackWidth = node.find('.rc-slider-track')[0].style.width;
expect(trackWidth).to.eql(node.find('.rc-slider-handle')[0].style.left);
});
it('should render a slider with correct numbers of step!', function () {
var slider = ReactDOM.render(
<Slider className='rc-slider' step={20}/>,
div
);
var node = $(div);
expect(node.find('.rc-slider').length).to.be(1);
expect(node.find('.rc-slider-handle').length).to.be(1);
expect(node.find('.rc-slider-track').length).to.be(1);
expect(slider.state.upperBound).to.be(0);
var sliderWithDots = ReactDOM.render(
<Slider className='rc-slider' step={20} dots/>,
div
);
var node1 = $(div);
expect(node1.find('.rc-slider-dot').length).to.be(6);
});
it('should render a slider with marks correctly!', function () {
var slider = ReactDOM.render(
<Slider marks={{0: "一", 20: "二", 40: "三", 60: "四", 100: "五"}} defaultValue={40} />,
div
);
var node = $(div);
expect(node.find('.rc-slider').length).to.be(1);
expect(node.find('.rc-slider-handle').length).to.be(1);
expect(node.find('.rc-slider-track').length).to.be(1);
expect(node.find('.rc-slider-dot').length).to.be(Object.keys(slider.props.marks).length);
expect(node.find('.rc-slider-mark').length).to.be(1);
expect(node.find('.rc-slider-mark-text').length).to.be(Object.keys(slider.props.marks).length);
expect(slider.state.upperBound).to.be(40);
});
// it('should mouseDown works!', function (done) {
// var slider = ReactDOM.render(
// <Slider marks={["一","二","三","四","五"]} />,
// div
// );
// var selectedStep = slider.refs.step3.getDOMNode();
// Simulate.mouseDown(selectedStep);
// setTimeout( function() {
// expect(slider.state.active).to.be('active');
// done();
// }, 200);
// });
});
-1
View File
@@ -1 +0,0 @@
stub