Compare commits

..
108 Commits
Author SHA1 Message Date
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
Benjy Cui 6f39212df5 Merge pull request #43 from react-component/fix-marks
fix: the key of marks could be negative
2015-11-18 18:37:46 +08:00
Benjy Cui a7c2319949 fix: the key of marks could be negative 2015-11-18 18:27:37 +08:00
Benjy Cui ee71955e14 Merge pull request #42 from react-component/3.0.0-beta
bump 3.0.0
2015-11-18 14:48:52 +08:00
Benjy Cui 8448e80212 bump 3.0.0 2015-11-18 14:38:52 +08:00
Benjy Cui 99e61001fc chore: update unit test 2015-11-18 14:32:56 +08:00
Benjy Cui 1546546040 refactor: remove useless code 2015-11-18 14:32:56 +08:00
Benjy Cui d4e2aad482 chore: update examples 2015-11-18 14:32:56 +08:00
Benjy Cui 7680347dfe feat: remove index/defaultIndex 2015-11-18 14:32:56 +08:00
Benjy Cui e2530ad2bf feat: Slider with marks should pass value(not index) to event handles 2015-11-18 14:32:56 +08:00
Benjy Cui 0566e7f098 fix: should trigger event after the value is changed 2015-11-18 14:32:56 +08:00
Benjy Cui 1e430ba694 fix: if there is no marks, step=1 should work 2015-11-18 14:32:56 +08:00
Benjy Cui f29894e690 chore: update tooltip className 2015-11-18 14:32:56 +08:00
Benjy Cui fd6c393f66 feat: Steps should not depend on marks 2015-11-18 14:32:56 +08:00
Benjy Cui e3ba1eee5b feat: marks should be an object 2015-11-18 14:32:56 +08:00
Benjy Cui 8dc66dd2a4 chore: remove isIncluded 2015-11-18 14:32:56 +08:00
afc163 b9501ef414 Merge pull request #40 from react-component/fix-controlled-slider
Fix controlled slider
2015-11-18 14:20:27 +08:00
Benjy Cui a329b00347 chore: update examples 2015-11-17 16:42:19 +08:00
Benjy Cui ccf3803d20 chore: remove initMochaPhantomJS 2015-11-17 11:41:55 +08:00
Benjy Cui b585f2fa1a bump 2.4.0 2015-11-17 10:35:02 +08:00
Benjy Cui 6f0e618a30 fix: Slider should be a controlled component 2015-11-17 10:31:10 +08:00
Benjy Cui ae4be7392d feat: unify onBeforeChange onChange onAfterChange 2015-11-17 09:54:09 +08:00
Benjy Cui d5891c79df chore: update examples 2015-11-17 09:48:35 +08:00
simaQ 87442eac35 Merge pull request #38 from react-component/chore
Chore
2015-11-16 18:24:47 +08:00
Benjy Cui 5e559a1555 chore: update test case 2015-11-16 16:35:17 +08:00
Benjy Cui 315849a0f3 refactor: rename variables 2015-11-16 16:33:45 +08:00
Benjy Cui 5f91e9a648 chore: update examples 2015-11-16 16:33:45 +08:00
yiminghe 610531be12 2.3.2 2015-11-16 14:05:16 +08:00
Benjy Cui a06b43dcd7 bump 2.3.1 2015-11-16 10:34:13 +08:00
Benjy Cui 3e11978bfc Merge pull request #37 from just-boris/master
feat: make range and marks props to work together
2015-11-16 10:02:06 +08:00
just-boris 48dab67950 make range and marks props to work together 2015-11-15 03:42:36 +03:00
26 changed files with 1212 additions and 410 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.
+40 -22
View File
@@ -88,11 +88,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 +106,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 +131,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 +142,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 +156,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>
+80 -42
View File
@@ -94,7 +94,7 @@
&-dot {
position: absolute;
top: -2px;
bottom: -2px;
margin-left: -4px;
width: 8px;
height: 8px;
@@ -131,53 +131,43 @@
cursor: not-allowed!important;
}
}
}
// slider tooltip style
&-tooltip {
position: absolute;
left: -9999px;
top: -9999px;
z-index: 4;
visibility: visible;
.borderBox();
&-hidden {
display: none;
.@{prefixClass}-vertical {
width: 4px;
height: 100%;
.@{prefixClass} {
&-track {
bottom: 0;
width: 4px;
}
&-placement-top {
padding: @tooltip-arrow-width 0 @tooltip-distance 0;
}
&-inner {
padding: 6px 2px;
min-width: 24px;
height: 24px;
font-size: 12px;
line-height: 1;
color: @tooltip-color;
text-align: center;
text-decoration: none;
background-color: @tooltip-bg;
border-radius: @border-radius-base;
box-shadow: 0 0 4px #d9d9d9;
}
&-arrow {
&-handle {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
margin-left: -5px;
margin-bottom: -7px;
}
&-placement-top &-arrow {
bottom: @tooltip-distance - @tooltip-arrow-width;
left: 50%;
margin-left: -@tooltip-arrow-width;
border-width: @tooltip-arrow-width @tooltip-arrow-width 0;
border-top-color: @tooltip-arrow-color;
&-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;
}
}
}
}
@@ -241,3 +231,51 @@
transform: scale(0, 0);
}
}
.rc-tooltip {
position: absolute;
left: -9999px;
top: -9999px;
z-index: 4;
visibility: visible;
.borderBox();
&-hidden {
display: none;
}
&-placement-top {
padding: @tooltip-arrow-width 0 @tooltip-distance 0;
}
&-inner {
padding: 6px 2px;
min-width: 24px;
height: 24px;
font-size: 12px;
line-height: 1;
color: @tooltip-color;
text-align: center;
text-decoration: none;
background-color: @tooltip-bg;
border-radius: @border-radius-base;
box-shadow: 0 0 4px #d9d9d9;
}
&-arrow {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
&-placement-top &-arrow {
bottom: @tooltip-distance - @tooltip-arrow-width;
left: 50%;
margin-left: -@tooltip-arrow-width;
border-width: @tooltip-arrow-width @tooltip-arrow-width 0;
border-top-color: @tooltip-arrow-color;
}
}
+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'));
+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 = {width: 400, margin: 50};
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>
<div style={style}>
<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</p>
<Slider dots min={-10} marks={marks} step={10} onChange={log} defaultValue={20} />
</div>
<div style={style}>
<p>Slider with marks, `included=false`</p>
<Slider min={-10} marks={marks} included={false} defaultValue={20} />
</div>
<div style={style}>
<p>Slider with marks and steps, `included=false`</p>
<Slider min={-10} marks={marks} step={10} included={false} defaultValue={20} />
</div>
<div style={style}>
<p>Range with marks</p>
<Slider min={-10} range marks={marks} onChange={log} defaultValue={[20, 40]} />
</div>
<div style={style}>
<p>Range with marks and steps</p>
<Slider min={-10} range marks={marks} step={10} onChange={log} defaultValue={[20, 40]} />
</div>
</div>
, document.getElementById('__react-content'));
+1
View File
@@ -0,0 +1 @@
placeholder
+117
View File
@@ -0,0 +1,117 @@
/* 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 = {width: 400, margin: 50};
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>
<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 (
<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`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, 20]} onBeforeChange={log} />
</div>
<div style={style}>
<p>Basic Range`step=20, dots` </p>
<Slider range dots step={20} defaultValue={[20, 40]} onAfterChange={log} />
</div>
<div style={style}>
<p>Controlled Range</p>
<Slider range 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'));
-51
View File
@@ -1,51 +0,0 @@
'use strict';
require('rc-slider/assets/index.less');
var React = require('react');
var ReactDOM = require('react-dom');
var Slider = require('rc-slider');
var style = {width:400,margin:50};
var log = console.log.bind(console);
function percentFormatter(v) {
return v + " %";
}
ReactDOM.render(
<div>
<div style={style}>
<p>基础滑块</p>
<Slider onChange={log} tipTransitionName='rc-slider-tooltip-zoom-down' />
</div>
<div style={style}>
<p>基础滑块step=20</p>
<Slider step={20} />
</div>
<div style={style}>
<p>基础滑块step=20 带圆点</p>
<Slider dots step={20} />
</div>
<div style={style}>
<p>双滑块</p>
<Slider range min={10} max={90} defaultValue={[0, 30]} onChange={log} />
</div>
<div style={style}>
<p>双滑块step=20 </p>
<Slider range dots step={20} defaultValue={[0, 30]} onAfterChange={log} included={false} />
</div>
<div style={style}>
<p>分段式滑块包含关系</p>
<Slider marks={["状态1","状态2","状态3","状态4"]} defaultIndex={1} />
</div>
<div style={style}>
<p>分段式滑块并列关系</p>
<Slider marks={["状态1","状态2","状态3","状态4"]} included={false} defaultIndex={1} />
</div>
<div style={style}>
<p>基础滑块</p>
<Slider onChange={log} tipFormatter={percentFormatter} tipTransitionName='rc-slider-tooltip-zoom-down' />
</div>
</div>
, document.getElementById('__react-content'));
+1
View File
@@ -0,0 +1 @@
placeholder
+109
View File
@@ -0,0 +1,109 @@
/* 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 = {width: 400, margin: 50};
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,
});
},
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 (
<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} />
</div>
<div style={style}>
<p>Basic Slider`step=20`</p>
<Slider step={20} defaultValue={50} onBeforeChange={log} />
</div>
<div style={style}>
<p>Basic Slider`step=20, dots`</p>
<Slider dots step={20} defaultValue={100} onAfterChange={log} />
</div>
<div style={style}>
<p>Basic Slider with `tipFormatter`</p>
<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>
<Slider value={50} />
</div>
<div style={style}>
<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'));
+8 -7
View File
@@ -1,6 +1,6 @@
{
"name": "rc-slider",
"version": "2.2.1",
"version": "3.8.1",
"description": "slider ui component for react",
"keywords": [
"react",
@@ -39,19 +39,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('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,
+35 -23
View File
@@ -1,35 +1,47 @@
import React from 'react';
import rcUtil from 'rc-util';
import classNames from 'classnames';
const Marks = ({className, marks, index, included}) => {
const marksLen = marks.length;
const unit = 100 / (marksLen - 1);
const markWidth = unit / 2 + '%';
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 elements = [];
for (let i = 0; i < marksLen; i++) {
const isActived = (included && i <= index) || (!included && i === index);
const markClassName = rcUtil.classSet({
const range = max - min;
const elements = marksKeys.map(parseFloat).sort((a, b) => a - b).map((point) => {
const isActived = (!included && point === upperBound) ||
(included && point <= upperBound && point >= lowerBound);
const markClassName = classNames({
[className + '-text']: true,
[className + '-text-active']: isActived,
});
const style = { width: markWidth };
const offset = unit * i;
if (i === marksLen - 1) {
style.right = -unit / 4 + '%';
} else {
style.left = (i > 0 ? offset - unit / 4 : -unit / 4) + '%';
}
const bottomStyle = {
// height: markWidth + '%',
marginBottom: '-200' + '%',
bottom: (point - min) / range * 100 + '%',
};
elements.push(<span className={markClassName} style={style} key={i}>
{marks[i]}
</span>);
}
const leftStyle = {
width: markWidth + '%',
marginLeft: -markWidth / 2 + '%',
left: (point - min) / range * 100 + '%',
};
return (<div className={className}>
{elements}
</div>);
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>);
});
return <div className={className}>{elements}</div>;
};
export default Marks;
+219 -163
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) {
@@ -29,30 +30,23 @@ class Slider extends React.Component {
constructor(props) {
super(props);
const {range, min, max} = props;
const initialValue = range ? [min, min] : min;
const defaultValue = ('defaultValue' in props ? props.defaultValue : initialValue);
const value = (props.value !== undefined ? props.value : defaultValue);
let upperBound;
let lowerBound;
if (props.range) {
const value = (props.value || props.defaultValue || [0, 0]);
upperBound = this.trimAlignValue(value[1]);
lowerBound = this.trimAlignValue(value[0]);
} else if (props.marks.length > 0) {
upperBound = this.calcValueFromProps(props);
upperBound = this.trimAlignValue(value[1]);
} else {
// Note: Maybe `value` is `0`.
// So, check the existence of `value` with `in`.
const defaultValue = ('defaultValue' in props ? props.defaultValue : 0);
const value = ('value' in props ? props.value : defaultValue);
upperBound = this.trimAlignValue(value);
}
let recent;
if (props.range && upperBound === lowerBound) {
if (lowerBound === props.max) {
recent = 'lowerBound';
}
if (upperBound === props.min) {
recent = 'upperBound';
}
recent = lowerBound === max ? 'lowerBound' : 'upperBound';
} else {
recent = 'upperBound';
}
@@ -62,32 +56,59 @@ class Slider extends React.Component {
recent: recent,
upperBound: upperBound,
// If Slider is not range, set `lowerBound` equal to `min`.
lowerBound: (lowerBound || props.min),
lowerBound: (lowerBound || min),
};
}
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],
});
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: nextUpperBound,
lowerBound: nextLowerBound,
});
if (this.isValueOutOfBounds(upperBound, nextProps) ||
this.isValueOutOfBounds(lowerBound, nextProps)) {
this.props.onChange([nextLowerBound, nextUpperBound]);
}
} else if ('value' in nextProps) {
} 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: nextProps.value,
});
} else if ('index' in nextProps) {
this.setState({
upperBound: this.calcValueFromProps(nextProps),
upperBound: nextValue,
lowerBound: nextProps.min,
});
if (this.isValueOutOfBounds(upperBound, nextProps)) {
this.props.onChange(nextValue);
}
}
}
onChange(state) {
const props = this.props;
const isNotControlled = !('value' in props);
if (isNotControlled) {
this.setState(state);
} else if (state.handle) {
this.setState({handle: state.handle});
}
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);
}
@@ -97,7 +118,7 @@ class Slider extends React.Component {
return;
}
const position = getTouchPosition(e);
const position = getTouchPosition(this.props.vertical, e);
this.onMove(e, position);
}
@@ -106,38 +127,56 @@ 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;
if (!('value' in props) && !('index' in props)) {
this.setState({[state.handle]: value}, () => {
this.triggerEvents('onChange', this.getValue());
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);
}
onSliderMouseDown(e) {
const position = getMousePosition(e);
onMouseDown(e) {
if (e.button !== 0) { return; }
const position = getMousePosition(this.props.vertical, e);
this.onStart(position);
this.addDocumentEvents('mouse');
pauseEvent(e);
}
onStart(position) {
this.triggerEvents('onBeforeChange');
const props = this.props;
props.onBeforeChange(this.getValue());
const value = this.calcValueByPos(position);
this.startValue = value;
@@ -166,9 +205,13 @@ class Slider extends React.Component {
this.setState({
handle: valueNeedChanging,
recent: valueNeedChanging,
});
const oldValue = state[valueNeedChanging];
if (value === oldValue) return;
this.onChange({
[valueNeedChanging]: value,
}, () => {
this.triggerEvents('onChange', this.getValue());
});
}
@@ -177,37 +220,39 @@ class Slider extends React.Component {
return this.props.range ? [lowerBound, upperBound] : upperBound;
}
getIndex(value) {
const {marks, min, max, step} = this.props;
if (marks.length === 0) {
return Math.floor((value - min) / step);
}
const unit = ((max - min) / (marks.length - 1)).toFixed(5);
return Math.round(value / unit);
}
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 props = this.props;
const {marks, min, max} = props;
const step = marks.length > 0 ? (max - min) / (marks.length - 1) : props.step;
const {handle, lowerBound, upperBound} = state;
const {marks, step, min, max, allowCross} = {...this.props, ...(nextProps || {})};
let val = v;
if (val <= min) {
@@ -216,21 +261,23 @@ class Slider extends React.Component {
if (val >= max) {
val = max;
}
if (state.handle === 'upperBound' && val <= state.lowerBound) {
val = state.lowerBound;
if (!allowCross && handle === 'upperBound' && val <= lowerBound) {
val = lowerBound;
}
if (state.handle === 'lowerBound' && val >= state.upperBound) {
val = state.upperBound;
if (!allowCross && handle === 'lowerBound' && val >= upperBound) {
val = upperBound;
}
const valModStep = (val - min) % step;
let alignValue = val - valModStep;
if (Math.abs(valModStep) * 2 >= step) {
alignValue += (valModStep > 0) ? step : (-step);
const points = Object.keys(marks).map(parseFloat);
if (step !== null) {
const closestStep = (Math.round((val - min) / step) * step) + min;
points.push(closestStep);
}
return parseFloat(alignValue.toFixed(5));
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(step))) : closestPoint;
}
calcOffset(value) {
@@ -240,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) {
@@ -251,45 +299,18 @@ class Slider extends React.Component {
return nextValue;
}
calcValueFromProps(props) {
const marksLen = props.marks.length;
if (marksLen > 0) {
const index = ('index' in props ? props.index : props.defaultIndex);
const value = ((props.max - props.min) / (marksLen - 1)) * (index);
// `'1' / 1 => 1`, to make sure that the returned value is a `Number`.
return value.toFixed(5) / 1;
}
return ('value' in props ? props.value : props.defaultValue);
}
triggerEvents(event, v) {
const props = this.props;
const hasMarks = (props.marks.length > 0);
if (props[event]) {
let data;
if (hasMarks) {
data = this.getIndex(v);
} else if (v === undefined) {
data = this.state.value;
} else {
data = v;
}
props[event](data);
}
}
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();
@@ -300,71 +321,101 @@ class Slider extends React.Component {
}
end(type) {
this.removeEventons(type);
this.triggerEvents('onAfterChange', this.getValue());
this.removeEvents(type);
this.props.onAfterChange(this.getValue());
this.setState({handle: null});
}
render() {
const {handle, upperBound, lowerBound} = this.state;
const props = this.props;
const {className, prefixCls, disabled, included, isIncluded, dots, range} = props;
const {marks, step, max, min, tipTransitionName, tipFormatter, children} = props;
const marksLen = 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 && isIncluded) || range) {
const trackClassName = prefixCls + '-track';
track = <Track className={trackClassName} offset={lowerOffset} length={upperOffset - lowerOffset}/>;
}
const handleClassName = prefixCls + '-handle';
const isNoTip = (marksLen > 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'} />);
}
const upperIndex = this.getIndex(upperBound);
let steps = null;
if (marksLen > 0 || (step > 1 && dots)) {
const stepsClassName = prefixCls + '-step';
const stepNum = marksLen > 0 ? marksLen : Math.floor((max - min) / step) + 1;
steps = (<Steps className={stepsClassName} stepNum={stepNum}
lowerIndex={this.getIndex(lowerBound)} upperIndex={upperIndex}
included={(included && isIncluded) || range}/>);
}
let mark = null;
if (marksLen > 0) {
const markClassName = prefixCls + '-mark';
mark = (<Marks className={markClassName} marks={marks}
index={upperIndex} included={(included && isIncluded)}/>);
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.onSliderMouseDown.bind(this)}>
{track}
onTouchStart={disabled ? noop : this.onTouchStart.bind(this)}
onMouseDown={disabled ? noop : this.onMouseDown.bind(this)}
>
{upper}
{lower}
{steps}
{mark}
<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>
);
@@ -379,14 +430,11 @@ Slider.propTypes = {
React.PropTypes.number,
React.PropTypes.arrayOf(React.PropTypes.number),
]),
defaultIndex: React.PropTypes.number,
value: React.PropTypes.oneOfType([
React.PropTypes.number,
React.PropTypes.arrayOf(React.PropTypes.number),
]),
index: React.PropTypes.number,
marks: React.PropTypes.array,
isIncluded: React.PropTypes.bool, // @Deprecated
marks: React.PropTypes.object,
included: React.PropTypes.bool,
className: React.PropTypes.string,
prefixCls: React.PropTypes.string,
@@ -395,26 +443,34 @@ 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 = {
prefixCls: 'rc-slider',
className: '',
tipTransitionName: '',
min: 0,
max: 100,
step: 1,
defaultIndex: 0,
marks: [],
isIncluded: true, // @Deprecated
marks: {},
handle: <DefaultHandle />,
onBeforeChange: noop,
onChange: noop,
onAfterChange: noop,
tipFormatter: value => value,
included: true,
className: '',
prefixCls: 'rc-slider',
disabled: false,
tipTransitionName: '',
dots: false,
range: false,
vertical: false,
allowCross: true,
};
export default Slider;
+27 -18
View File
@@ -1,28 +1,37 @@
import React from 'react';
import rcUtil from 'rc-util';
import classNames from 'classnames';
import warning from 'warning';
const Steps = ({className, stepNum, included, lowerIndex, upperIndex}) => {
const dotClassName = className.replace('step', 'dot');
const unit = 100 / (stepNum - 1);
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 elements = [];
for (let i = 0; i < stepNum; i++) {
const offset = unit * i + '%';
const style = { left: offset };
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 && i <= upperIndex && i >= lowerIndex ) ||
(!included && i === upperIndex);
const stepClassName = rcUtil.classSet({
[dotClassName]: true,
[dotClassName + '-active']: isActived,
const isActived = (!included && point === upperBound) ||
(included && point <= upperBound && point >= lowerBound);
const pointClassName = classNames({
[prefixCls + '-dot']: true,
[prefixCls + '-dot-active']: isActived,
});
elements.push(<span className={stepClassName} style={style} key={i} />);
}
return <span className={pointClassName} style={style} key={point} />;
});
return (<div className={className}>
{elements}
</div>);
return <div className={prefixCls + '-step'}>{elements}</div>;
};
export default Steps;
+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} />;
};
+129 -64
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 () {
React.unmountComponentAtNode(div);
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={["一","二","三","四","五"]} defaultIndex={3} />,
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(slider.props.marks.length);
expect(node.find('.rc-slider-mark').length).to.be(1);
expect(node.find('.rc-slider-mark-text').length).to.be(slider.props.marks.length);
expect(slider.getIndex(slider.state.upperBound)).to.be(3);
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);
});
});