Compare commits

..
81 Commits
Author SHA1 Message Date
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
Benjy Cui ee0dd09a4f fix: support decimal step 2015-11-23 16:00:21 +08:00
Benjy Cui a5de9b0dbc Merge pull request #48 from react-component/improve-performance
refactor: improve performance
2015-11-23 15:37:13 +08:00
Benjy Cui 37d32ae71c Merge pull request #49 from just-boris/master
style: reduce gaps between marks
2015-11-23 09:57:24 +08:00
just-boris f339db494b reduce gaps between marks 2015-11-23 00:17:29 +03:00
Benjy Cui c097b8f761 bump 3.1.1 2015-11-22 10:31:08 +08:00
Benjy Cui 10244cf01b refactor: improve the performance of Slider 2015-11-22 10:29:41 +08:00
Benjy Cui e8a86b91e2 Merge pull request #45 from react-component/feat-mark-as-step
Feat mark as step
2015-11-19 11:55:10 +08:00
Benjy Cui 05dafc0f10 bump 3.1.0 2015-11-19 11:43:25 +08:00
Benjy Cui 5dbde43da8 docs: update README.md 2015-11-19 11:43:07 +08:00
Benjy Cui 0a23a67f31 feat: allow user set step to null to make marks as steps 2015-11-19 11:42:04 +08:00
22 changed files with 1013 additions and 285 deletions
+5
View File
@@ -1,6 +1,11 @@
# History
----
## 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.
+42 -22
View File
@@ -50,6 +50,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');
@@ -88,11 +90,17 @@ ReactDOM.render(<Rcslider />, container);
<td>100</td>
<td>The maximum value of the slider</td>
</tr>
<tr>
<td>marks</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. 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>
<td>number</td>
<td>number or `null`</td>
<td>1</td>
<td>Value to be added or subtracted on each step the slider makes. Must be greater than zero. max - min should be evenly divisible by the step value.</td>
<td>Value to be added or subtracted on each step the slider makes. Must be greater than zero. max - min should be evenly divisible by the step value. When `marks` is not an empty object, `step` can be set to `null`, to make marks as steps.</td>
</tr>
<tr>
<td>range</td>
@@ -100,6 +108,18 @@ ReactDOM.render(<Rcslider />, container);
<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>
</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 two handles cross.</td>
</tr>
<tr>
<td>vertical</td>
<td>boolean</td>
<td>false</td>
<td>If vertical is `true`, the slider will be vertical.</td>
</tr>
<tr>
<td>defaultValue</td>
<td>number or [number, number]</td>
@@ -113,10 +133,10 @@ ReactDOM.render(<Rcslider />, container);
<td>Set current positions of handles. If range is `false`, the type of `defaultValue` should be `number`. Otherwise, `[number, number]`</td>
</tr>
<tr>
<td>marks</td>
<td>array</td>
<td>[]</td>
<td>Mark every step for the slider, it will ignore the `step` parameter if it has been defined. Does not work with `range`</td>
<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>
@@ -124,23 +144,11 @@ ReactDOM.render(<Rcslider />, container);
<td>true</td>
<td>If the value is `true`, it means a continuous value interval, otherwise, it is a independent value.</td>
</tr>
<tr>
<td>defaultIndex</td>
<td>number</td>
<td>0</td>
<td>For step or marks slider, determines the initial position of the handle. Does not work with `range`</td>
</tr>
<tr>
<td>index</td>
<td>number</td>
<td></td>
<td>For step or marks slider, determines current position of the handle. Does not work with `range`</td>
</tr>
<tr>
<td>disabled</td>
<td>boolean</td>
<td>false</td>
<td>If true the handles can't be moved.</td>
<td>If `true`, handles can't be moved.</td>
</tr>
<tr>
<td>tipTransitionName</td>
@@ -150,15 +158,27 @@ ReactDOM.render(<Rcslider />, container);
</tr>
<tr>
<td>tipFormatter</td>
<td>func</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>
<td>bool</td>
<td>false</td>
<td>For linear slider, when the `step` value is greater than 1, you can set the `dots` to `true` if you want to render the slider bar with dots.</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>
+40 -1
View File
@@ -94,7 +94,7 @@
&-dot {
position: absolute;
top: -2px;
bottom: -2px;
margin-left: -4px;
width: 8px;
height: 8px;
@@ -133,6 +133,45 @@
}
}
.@{prefixClass}-vertical {
width: 4px;
height: 100%;
.@{prefixClass} {
&-track {
bottom: 0;
width: 4px;
}
&-handle {
position: absolute;
margin-left: -5px;
margin-bottom: -7px;
}
&-mark {
top: 0;
left: 10px;
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;
+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',
zIndex: 3,
};
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'));
+19 -15
View File
@@ -1,33 +1,37 @@
'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>
<div style={style}>
<p>Slider with marks, `included=true`</p>
<Slider min={-10} marks={marks} onChange={log} defaultValue={20} />
<p>Slider with marks, `step=null`</p>
<Slider min={-10} marks={marks} step={null} onChange={log} defaultValue={20} />
</div>
<div style={style}>
<p>Slider with marks and steps, `included=true`</p>
<Slider min={-10} marks={marks} step={10} onChange={log} defaultValue={20} />
<p>Slider with marks and steps</p>
<Slider dots min={-10} marks={marks} step={10} onChange={log} defaultValue={20} />
</div>
<div style={style}>
+81 -18
View File
@@ -1,42 +1,101 @@
'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({
const CustomizedRange = React.createClass({
getInitialState: function() {
return {
value: [20, 40]
}
lowerBound: 20,
upperBound: 40,
value: [20, 40],
};
},
onChange: function(value) {
onLowerBoundChange: function(e) {
this.setState({ lowerBound: +e.target.value });
},
onUpperBoundChange: function(e) {
this.setState({ upperBound: +e.target.value });
},
onSliderChange: function(value) {
log(value);
this.setState({
value: value
value: value,
});
},
handleApply: function() {
const { lowerBound, upperBound } = this.state;
this.setState({ value: [lowerBound, upperBound]});
},
render: function() {
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: function(value) {
log(value);
},
onMinChange: function(e) {
this.setState({
min: +e.target.value || 0,
});
},
onMaxChange: function(e) {
this.setState({
max: +e.target.value || 100,
});
},
render: function() {
return <Slider range value={this.state.value} onChange={this.onChange} />;
}
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>
@@ -50,5 +109,9 @@ ReactDOM.render(
<p>Customized Range</p>
<CustomizedRange />
</div>
<div style={style}>
<p>Range with dynamic `max` `min`</p>
<DynamicBounds />
</div>
</div>
, document.getElementById('__react-content'));
+63 -17
View File
@@ -1,43 +1,81 @@
'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 + ' %';
}
var CustomizedSlider = React.createClass({
const CustomizedSlider = React.createClass({
getInitialState: function() {
return {
value: 50
}
value: 50,
};
},
onChange: function(value) {
onSliderChange: function(value) {
log(value);
this.setState({
value: value
value: value,
});
},
onAfterChange: function(value) {
console.log(value);
},
render: function() {
return <Slider value={this.state.value} onChange={this.onSliderChange} onAfterChange={this.onAfterChange} />;
},
});
const DynamicBounds = React.createClass({
getInitialState: function() {
return {
min: 0,
max: 100,
};
},
onSliderChange: function(value) {
log(value);
},
onMinChange: function(e) {
this.setState({
min: +e.target.value || 0,
});
},
onMaxChange: function(e) {
this.setState({
max: +e.target.value || 100,
});
},
render: function() {
return <Slider value={this.state.value} onChange={this.onChange} />;
}
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 +87,11 @@ 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 +101,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
+55
View File
@@ -0,0 +1,55 @@
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
+116
View File
@@ -0,0 +1,116 @@
/* 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: function() {
return {
lowerBound: 20,
upperBound: 40,
value: [20, 40],
};
},
onLowerBoundChange: function(e) {
this.setState({ lowerBound: +e.target.value });
},
onUpperBoundChange: function(e) {
this.setState({ upperBound: +e.target.value });
},
onSliderChange: function(value) {
log(value);
this.setState({
value: value,
});
},
handleApply: function() {
const { lowerBound, upperBound } = this.state;
this.setState({ value: [lowerBound, upperBound]});
},
render: function() {
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: function(value) {
log(value);
},
onMinChange: function(e) {
this.setState({
min: +e.target.value || 0,
});
},
onMaxChange: function(e) {
this.setState({
max: +e.target.value || 100,
});
},
render: function() {
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
+106
View File
@@ -0,0 +1,106 @@
/* 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: function() {
return {
value: 50,
};
},
onSliderChange: function(value) {
log(value);
this.setState({
value: value,
});
},
render: function() {
return <Slider vertical value={this.state.value} onChange={this.onSliderChange} />;
},
});
const DynamicBounds = React.createClass({
getInitialState: function() {
return {
min: 0,
max: 100,
};
},
onSliderChange: function(value) {
log(value);
},
onMinChange: function(e) {
this.setState({
min: +e.target.value || 0,
});
},
onMaxChange: function(e) {
this.setState({
max: +e.target.value || 100,
});
},
render: function() {
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'));
+9 -7
View File
@@ -1,6 +1,6 @@
{
"name": "rc-slider",
"version": "3.0.1",
"version": "3.8.2",
"description": "slider ui component for react",
"keywords": [
"react",
@@ -23,6 +23,7 @@
],
"licenses": "MIT",
"main": "./lib/index.js",
"style": "./assets/index.css",
"config": {
"port": 8005
},
@@ -39,19 +40,20 @@
},
"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"
"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"
"classnames": "^2.2.5",
"rc-tooltip": "^3.4.2",
"rc-util": "^3.2.1",
"warning": "^3.0.0"
}
}
+33 -17
View File
@@ -23,35 +23,51 @@ 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,
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={prefixCls.replace('slider', 'tooltip')}
placement="top"
visible={isTooltipVisible}
overlay={<span>{tipFormatter(value)}</span>}
delay={0}
transitionName={tipTransitionName}
>
{handle}
</Tooltip>
);
}
}
Handle.propTypes = {
prefixCls: React.PropTypes.string,
className: React.PropTypes.string,
vertical: React.PropTypes.bool,
offset: React.PropTypes.number,
tipTransitionName: React.PropTypes.string,
tipFormatter: React.PropTypes.func,
+26 -9
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 / 2 + '%';
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({
const markClassName = classNames({
[className + '-text']: true,
[className + '-text-active']: isActived,
});
const style = { width: markWidth };
style.left = (point - min) / range * 100 - unit / 4 + '%';
const bottomStyle = {
// height: markWidth + '%',
marginBottom: '-200' + '%',
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>);
});
+187 -99
View File
@@ -1,7 +1,8 @@
import React from 'react';
import {Dom as DomUtils, classSet} from 'rc-util';
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 DefaultHandle from './Handle';
import Steps from './Steps';
import Marks from './Marks';
@@ -12,12 +13,12 @@ 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 pauseEvent(e) {
@@ -25,10 +26,6 @@ function pauseEvent(e) {
e.preventDefault();
}
function isEmpty(collection) {
return Object.keys(collection).length === 0;
}
class Slider extends React.Component {
constructor(props) {
super(props);
@@ -36,7 +33,7 @@ class Slider extends React.Component {
const {range, min, max} = props;
const initialValue = range ? [min, 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;
@@ -49,12 +46,7 @@ class Slider extends React.Component {
let recent;
if (props.range && upperBound === lowerBound) {
if (lowerBound === max) {
recent = 'lowerBound';
}
if (upperBound === min) {
recent = 'upperBound';
}
recent = lowerBound === max ? 'lowerBound' : 'upperBound';
} else {
recent = 'upperBound';
}
@@ -69,40 +61,54 @@ class Slider extends React.Component {
}
componentWillReceiveProps(nextProps) {
if (!('value' in nextProps || 'min' in nextProps || 'max' in nextProps)) return;
const {lowerBound, upperBound} = this.state;
if (nextProps.range) {
const value = nextProps.value;
if (value) {
this.setState({
upperBound: value[1],
lowerBound: value[0],
});
}
} else if ('value' in nextProps) {
const value = nextProps.value || [lowerBound, upperBound];
const nextUpperBound = this.trimAlignValue(value[1], nextProps);
const nextLowerBound = this.trimAlignValue(value[0], nextProps);
if (nextLowerBound === lowerBound && nextUpperBound === upperBound) return;
this.setState({
upperBound: nextProps.value,
upperBound: nextUpperBound,
lowerBound: nextLowerBound,
});
if (this.isValueOutOfBounds(upperBound, nextProps) ||
this.isValueOutOfBounds(lowerBound, nextProps)) {
this.props.onChange([nextLowerBound, nextUpperBound]);
}
} else {
const value = nextProps.value !== undefined ? nextProps.value : upperBound;
const nextValue = this.trimAlignValue(value, nextProps);
if (nextValue === upperBound && lowerBound === nextProps.min) return;
this.setState({
upperBound: nextValue,
lowerBound: nextProps.min,
});
if (this.isValueOutOfBounds(upperBound, nextProps)) {
this.props.onChange(nextValue);
}
}
}
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) {
this.setState({handle: state.handle});
}
const state = this.state;
const data = {
upperBound: state.upperBound,
lowerBound: state.lowerBound,
};
data[handle] = value;
const data = {...this.state, ...state};
const changedValue = props.range ? [data.lowerBound, data.upperBound] : data.upperBound;
props.onChange(changedValue);
}
onMouseMove(e) {
const position = getMousePosition(e);
const position = getMousePosition(this.props.vertical, e);
this.onMove(e, position);
}
@@ -112,7 +118,7 @@ class Slider extends React.Component {
return;
}
const position = getTouchPosition(e);
const position = getTouchPosition(this.props.vertical, e);
this.onMove(e, position);
}
@@ -121,27 +127,48 @@ 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];
if (value === oldValue) return;
this.onChange(state.handle, value);
if (props.allowCross && value < state.lowerBound && state.handle === 'upperBound') {
this.onChange({
handle: 'lowerBound',
lowerBound: value,
upperBound: this.state.lowerBound,
});
return;
}
if (props.allowCross && value > state.upperBound && state.handle === 'lowerBound') {
this.onChange({
handle: 'upperBound',
upperBound: value,
lowerBound: this.state.upperBound,
});
return;
}
this.onChange({
[state.handle]: value,
});
}
onTouchStart(e) {
if (isNotTouchEvent(e)) return;
const position = getTouchPosition(e);
const position = getTouchPosition(this.props.vertical, e);
this.onStart(position);
this.addDocumentEvents('touch');
pauseEvent(e);
}
onMouseDown(e) {
const position = getMousePosition(e);
if (e.button !== 0) { return; }
const position = getMousePosition(this.props.vertical, e);
this.onStart(position);
this.addDocumentEvents('mouse');
pauseEvent(e);
@@ -183,7 +210,9 @@ class Slider extends React.Component {
const oldValue = state[valueNeedChanging];
if (value === oldValue) return;
this.onChange(valueNeedChanging, value);
this.onChange({
[valueNeedChanging]: value,
});
}
getValue() {
@@ -191,37 +220,39 @@ class Slider extends React.Component {
return this.props.range ? [lowerBound, upperBound] : upperBound;
}
getPoints() {
const {marks, step, min, max} = this.props;
const points = Object.keys(marks);
if (isEmpty(marks) || step > 1) {
for (let i = min; i <= max; i = i + step) {
points.push(i);
}
}
return points;
}
getSliderLength() {
const slider = this.refs.slider;
if (!slider) {
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;
}
trimAlignValue(v) {
getPrecision(step) {
const stepString = step.toString();
let precision = 0;
if (stepString.indexOf('.') >= 0) {
precision = stepString.length - stepString.indexOf('.') - 1;
}
return precision;
}
isValueOutOfBounds(value, props) {
return value < props.min || value > props.max;
}
trimAlignValue(v, nextProps) {
const state = this.state || {};
const {handle, lowerBound, upperBound} = state;
const {min, max} = this.props;
const {marks, step, min, max, allowCross} = {...this.props, ...(nextProps || {})};
let val = v;
if (val <= min) {
@@ -230,18 +261,23 @@ class Slider extends React.Component {
if (val >= max) {
val = max;
}
if (handle === 'upperBound' && val <= lowerBound) {
if (!allowCross && handle === 'upperBound' && val <= lowerBound) {
val = lowerBound;
}
if (handle === 'lowerBound' && val >= upperBound) {
if (!allowCross && handle === 'lowerBound' && val >= upperBound) {
val = upperBound;
}
const points = this.getPoints().map(parseFloat);
const points = Object.keys(marks).map(parseFloat);
if (step !== null) {
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 closestPoint;
return step !== null ? parseFloat(closestPoint.toFixed(this.getPrecision(step))) : closestPoint;
}
calcOffset(value) {
@@ -251,9 +287,10 @@ class Slider extends React.Component {
}
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,15 +302,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'));
}
}
removeEventons(type) {
removeEvents(type) {
if (type === 'touch') {
this.onTouchMoveListener.remove();
this.onTouchUpListener.remove();
@@ -284,57 +321,101 @@ class Slider extends React.Component {
}
end(type) {
this.removeEventons(type);
this.removeEvents(type);
this.props.onAfterChange(this.getValue());
this.setState({handle: null});
}
render() {
const {handle, upperBound, lowerBound} = this.state;
const {className, prefixCls, disabled, dots, included, range,
marks, max, min, tipTransitionName, tipFormatter, children} = this.props;
const marksCount = Object.keys(marks).length;
const {
handle,
upperBound,
lowerBound,
} = this.state;
const {
className,
prefixCls,
disabled,
vertical,
dots,
included,
range,
step,
marks,
max, min,
tipTransitionName,
tipFormatter,
children,
} = this.props;
const sliderClassName = classSet({
[prefixCls]: true,
[prefixCls + '-disabled']: disabled,
[className]: !!className,
});
const customHandle = this.props.handle;
const upperOffset = this.calcOffset(upperBound);
const lowerOffset = this.calcOffset(lowerBound);
let track = null;
if (included || range) {
const trackClassName = prefixCls + '-track';
track = <Track className={trackClassName} offset={lowerOffset} length={upperOffset - lowerOffset}/>;
}
const handleClassName = prefixCls + '-handle';
const isNoTip = (marksCount > 0) && !tipFormatter;
const upper = (<Handle className={handleClassName} tipTransitionName={tipTransitionName} noTip={isNoTip} tipFormatter={tipFormatter}
offset={upperOffset} value={upperBound} dragging={handle === 'upperBound'} />);
const upperClassName = classNames({
[handleClassName]: true,
[handleClassName + '-upper']: true,
});
const lowerClassName = classNames({
[handleClassName]: true,
[handleClassName + '-lower']: true,
});
const isNoTip = (step === null) || (tipFormatter === null);
const commonHandleProps = {
prefixCls,
noTip: isNoTip,
tipTransitionName,
tipFormatter,
vertical,
};
const upper = cloneElement(customHandle, {
...commonHandleProps,
className: upperClassName,
value: upperBound,
offset: upperOffset,
dragging: handle === 'upperBound',
});
let lower = null;
if (range) {
lower = (<Handle className={handleClassName} tipTransitionName={tipTransitionName} noTip={isNoTip} tipFormatter={tipFormatter}
offset={lowerOffset} value={lowerBound} dragging={handle === 'lowerBound'} />);
lower = cloneElement(customHandle, {
...commonHandleProps,
className: lowerClassName,
value: lowerBound,
offset: lowerOffset,
dragging: handle === 'lowerBound',
});
}
const sliderClassName = classNames({
[prefixCls]: true,
[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)}>
{track}
onTouchStart={disabled ? noop : this.onTouchStart.bind(this)}
onMouseDown={disabled ? noop : this.onMouseDown.bind(this)}
>
{upper}
{lower}
<Steps prefixCls={prefixCls} points={this.getPoints()} dots={dots}
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} />
<Track className={prefixCls + '-track'} vertical = {vertical} included={isIncluded}
offset={lowerOffset} length={upperOffset - lowerOffset} />
<Steps prefixCls={prefixCls} vertical = {vertical} marks={marks} dots={dots} step={step}
included={isIncluded} lowerBound={lowerBound}
upperBound={upperBound} max={max} min={min} />
<Marks className={prefixCls + '-mark'} vertical = {vertical} marks={marks}
included={isIncluded} lowerBound={lowerBound}
upperBound={upperBound} max={max} min={min} />
{children}
</div>
);
@@ -362,10 +443,13 @@ Slider.propTypes = {
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,
vertical: React.PropTypes.bool,
allowCross: React.PropTypes.bool,
};
Slider.defaultProps = {
@@ -376,13 +460,17 @@ 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,
};
export default Slider;
+27 -14
View File
@@ -1,22 +1,35 @@
import React from 'react';
import { classSet } from 'rc-util';
import classNames from 'classnames';
import warning from 'warning';
const Steps = ({prefixCls, points, dots, included, lowerBound, upperBound, max, min}) => {
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 = points.filter((point) => typeof point === 'string' || dots).map(parseFloat)
.map((point) => {
const offset = (point - min) / range * 100 + '%';
const style = { left: offset };
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 = classSet({
[prefixCls + '-dot']: true,
[prefixCls + '-dot-active']: isActived,
});
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 <span className={pointClassName} style={style} key={point} />;
});
return <div className={prefixCls + '-step'}>{elements}</div>;
};
+9 -3
View File
@@ -1,10 +1,16 @@
import React from 'react';
const Track = ({className, 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} />;
};
+128 -63
View File
@@ -1,83 +1,148 @@
const expect = require('expect.js');
const React = require('react');
const ReactDOM = require('react-dom');
const ReactTestUtils = require('react-addons-test-utils');
const Slider = require('..');
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 () {
describe('rc-slider', function test() {
this.timeout(5000);
var div = document.createElement('div');
const div = document.createElement('div');
document.body.appendChild(div);
afterEach(function () {
afterEach(() => {
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 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 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);
it('should render a Slider with default value correctly', () => {
const sliderWithDefaultValue = ReactDOM.render(<Slider defaultValue={50} />, div);
expect(sliderWithDefaultValue.state.upperBound).to.be(50);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(sliderWithDefaultValue, 'rc-slider-handle')[0]
.style.cssText)
.to.match(/left: 50%;/);
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);
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 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 render a Slider with value corrently', () => {
const sliderWithValue = ReactDOM.render(<Slider value={50} />, div);
expect(sliderWithValue.state.upperBound).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 mouseDown works!', function (done) {
// var slider = ReactDOM.render(
// <Slider marks={["一","二","三","四","五"]} />,
// div
// );
// var selectedStep = slider.refs.step3.getDOMNode();
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);
});
// Simulate.mouseDown(selectedStep);
it('should render a Range with default value correctly', () => {
const rangeWithDefaultValue = ReactDOM.render(<Slider range defaultValue={[0, 50]} />, div);
expect(rangeWithDefaultValue.state.lowerBound).to.be(0);
expect(rangeWithDefaultValue.state.upperBound).to.be(50);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithDefaultValue, 'rc-slider-handle')[0]
.style.cssText)
.to.match(/left: 50%;/);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithDefaultValue, 'rc-slider-handle')[1]
.style.cssText)
.to.match(/left: 0%;/);
// setTimeout( function() {
// expect(slider.state.active).to.be('active');
// done();
// }, 200);
// });
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 Range with value correctly', () => {
const rangeWithValue = ReactDOM.render(<Slider range value={[50, 100]} />, div);
expect(rangeWithValue.state.lowerBound).to.be(50);
expect(rangeWithValue.state.upperBound).to.be(100);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithValue, 'rc-slider-handle')[0]
.style.cssText)
.to.match(/left: 100%;/);
expect(ReactTestUtils
.scryRenderedDOMComponentsWithClass(rangeWithValue, 'rc-slider-handle')[1]
.style.cssText)
.to.match(/left: 50%;/);
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.upperBound).to.be(10);
ReactDOM.unmountComponentAtNode(div);
const sliderWithMax = ReactDOM.render(<Slider value={100} max={90} />, div);
expect(sliderWithMax.state.upperBound).to.be(90);
ReactDOM.unmountComponentAtNode(div);
const range = ReactDOM.render(<Slider range value={[0, 100]} min={10} max={90} />, div);
expect(range.state.lowerBound).to.be(10);
expect(range.state.upperBound).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);
});
});