diff --git a/site/docs/en-US/slider.md b/site/docs/en-US/slider.md
index 2d9a350f7..5ca4c2af8 100644
--- a/site/docs/en-US/slider.md
+++ b/site/docs/en-US/slider.md
@@ -121,15 +121,15 @@ Selecting a range of values is supported.
constructor(props) {
super(props);
- this.state = {
- value: [4, 8]
- }
+ this.onChange = (value) => {
+ this.setState({ value });
+ };
}
render() {
return (
-
+
)
}
@@ -165,11 +165,12 @@ render() {
| max | maximum value | number | — | 100 |
| disabled | whether Slider is disabled | boolean | — | false |
| step | step size | number | — | 1 |
-| show-input | whether to display an input box, works when `range` is false | boolean | — | false |
-| show-input-controls | whether to display control buttons when `show-input` is true | boolean | — | true |
-| show-stops | whether to display breakpoints | boolean | — | false |
-| show-tooltip | whether to display tooltip value | boolean | — | true |
+| showInput | whether to display an input box, works when `range` is false | boolean | — | false |
+| showInputControls | whether to display control buttons when `show-input` is true | boolean | — | true |
+| showStops | whether to display breakpoints | boolean | — | false |
+| showTooltip | whether to display tooltip value | boolean | — | true |
| range | whether to select a range | boolean | — | false |
+| triggerOnDragging | trigger `onChange` when mouse dragging | boolean | — | false |
## Events
| Event Name | Description | Parameters |
diff --git a/site/docs/zh-CN/slider.md b/site/docs/zh-CN/slider.md
index 1fed43ec8..81550356b 100644
--- a/site/docs/zh-CN/slider.md
+++ b/site/docs/zh-CN/slider.md
@@ -121,12 +121,16 @@ constructor(props) {
this.state = {
value: [4, 8]
}
+
+ this.onChange = (value) => {
+ this.setState({ value });
+ };
}
render() {
return (
-
+
)
}
@@ -165,7 +169,9 @@ render() {
| showInput | 是否显示输入框,仅在非范围选择时有效 | boolean | — | false |
| showInputControls | 在显示输入框的情况下,是否显示输入框的控制按钮 | boolean | — | true|
| showStops | 是否显示间断点 | boolean | — | false |
+| showTooltip | 是否显示值提示 | boolean | — | true |
| range | 是否为范围选择 | boolean | — | false |
+| triggerOnDragging | 是否在拖动过程中触发 `onChange` | boolean | — | false |
### Events
| 事件名称 | 说明 | 回调参数 |
diff --git a/src/slider/Button.jsx b/src/slider/Button.jsx
index ed718df9c..78401a991 100644
--- a/src/slider/Button.jsx
+++ b/src/slider/Button.jsx
@@ -10,8 +10,6 @@ type State = {
dragging: boolean,
startX: number,
startY: number,
- currentX: number,
- currentY: number,
startPosition: number,
newPosition: number
}
@@ -31,8 +29,6 @@ export default class SliderButton extends Component {
dragging: false,
startX: 0,
startY: 0,
- currentX: 0,
- currentY: 0,
startPosition: 0,
newPosition: 0
}
@@ -42,58 +38,59 @@ export default class SliderButton extends Component {
return this.context.component;
}
- handleMouseEnter(): void {
+ handleMouseEnter = (): void => {
this.setState({
hovering: true
});
}
- handleMouseLeave(): void {
+ handleMouseLeave = (): void => {
this.setState({
hovering: false
});
}
- onButtonDown(event: SyntheticMouseEvent) {
+ onDragStart = (event: SyntheticMouseEvent) => {
if (this.disabled()) return;
- this.onDragStart(event);
+ const position = parseInt(this.currentPosition(), 10);
- window.addEventListener('mousemove', this.onDragging.bind(this));
- window.addEventListener('mouseup', this.onDragEnd.bind(this));
- window.addEventListener('contextmenu', this.onDragEnd.bind(this));
- }
-
- onDragStart(event: SyntheticMouseEvent) {
this.setState({
dragging: true,
startX: event.clientX,
startY: event.clientY,
- startPosition: parseInt(this.currentPosition(), 10)
+ startPosition: position,
+ newPosition: position
});
- }
- onDragging(event: SyntheticMouseEvent) {
- const { dragging, startY, currentY, currentX, startX, startPosition, newPosition } = this.state;
+ window.addEventListener('mousemove', this.onDragging);
+ window.addEventListener('mouseup', this.onDragEnd);
+ window.addEventListener('contextmenu', this.onDragEnd);
+ };
+
+ onDragging = (event: SyntheticMouseEvent) => {
+ const { dragging, startY, startX, startPosition } = this.state;
const { vertical } = this.props;
- if (dragging) {
- this.setState({
- currentX: event.clientX,
- currentY: event.clientY,
- }, () => {
- let diff;
- if (vertical) {
- diff = (startY - currentY) / this.parent().sliderSize() * 100;
- } else {
- diff = (currentX - startX) / this.parent().sliderSize() * 100;
- }
- this.state.newPosition = startPosition + diff;
- this.setPosition(newPosition);
- });
+ if (!dragging) {
+ return;
}
- }
+ const diff = vertical
+ ? (startY - event.clientY)
+ : (event.clientX - startX);
- onDragEnd() {
+ if (!diff) {
+ return;
+ }
+
+ const newPosition = startPosition + diff / this.parent().sliderSize() * 100;
+ this.setState({
+ newPosition
+ });
+
+ this.setPosition(newPosition);
+ };
+
+ onDragEnd = () => {
const { dragging, newPosition } = this.state;
if (dragging) {
/*
@@ -108,13 +105,14 @@ export default class SliderButton extends Component {
});
}, 0);
- window.removeEventListener('mousemove', this.onDragging.bind(this));
- window.removeEventListener('mouseup', this.onDragEnd.bind(this));
- window.removeEventListener('contextmenu', this.onDragEnd.bind(this));
+ window.removeEventListener('mousemove', this.onDragging);
+ window.removeEventListener('mouseup', this.onDragEnd);
+ window.removeEventListener('contextmenu', this.onDragEnd);
}
- }
+ };
setPosition(newPosition: number) {
+ const { onChange } = this.props;
if (newPosition < 0) {
newPosition = 0;
} else if (newPosition > 100) {
@@ -125,7 +123,7 @@ export default class SliderButton extends Component {
const steps = Math.round(newPosition / lengthPerStep);
const value = steps * lengthPerStep * (this.max() - this.min()) * 0.01 + this.min();
- this.props.onChange(parseFloat(value.toFixed(this.precision())));
+ onChange(parseFloat(value.toFixed(this.precision())));
}
/* Computed Methods */
@@ -178,9 +176,9 @@ export default class SliderButton extends Component {
'dragging': dragging
})}
style={this.wrapperStyle()}
- onMouseEnter={this.handleMouseEnter.bind(this)}
- onMouseLeave={this.handleMouseLeave.bind(this)}
- onMouseDown={this.onButtonDown.bind(this)}>
+ onMouseEnter={this.handleMouseEnter}
+ onMouseLeave={this.handleMouseLeave}
+ onMouseDown={this.onDragStart}>
{this.formatValue()}}
diff --git a/src/slider/Slider.jsx b/src/slider/Slider.jsx
index fdbe39e85..90bea1f63 100644
--- a/src/slider/Slider.jsx
+++ b/src/slider/Slider.jsx
@@ -6,152 +6,117 @@ import { Component, PropTypes } from '../../libs';
import InputNumber from '../input-number';
import SliderButton from './Button';
+type Value = number | Array;
+
type State = {
firstValue: number,
secondValue: number,
- oldValue: number | Array,
+ draggingValue: Value,
precision: number,
- inputValue: number,
- dragging: boolean,
+ inputValue: number
}
export default class Slider extends Component {
- state: State;
-
- constructor(props: Object) {
- super(props);
- this.slider = React.createRef();
- this.button1 = React.createRef();
- this.button2 = React.createRef();
- this.state = {
- firstValue: 0,
- secondValue: 0,
- oldValue: 0,
- precision: 0,
- inputValue: 0,
- dragging: false
- }
- }
-
- getChildContext(): Object {
- return {
- component: this
- };
- }
-
- get initValue() {
- const { value, min, max } = this.props;
- let initValue = value;
- if (typeof value !== 'number' || isNaN(value)) {
- initValue = min;
- } else {
- initValue = Math.min(max, Math.max(min, value));
- }
- return initValue;
- }
+ static getDerivedStateFromProps(props: Object, state: State): State {
+ const { range, value, min, max, step } = props;
- componentWillMount(): void {
- const { range, value, min, max, step } = this.props;
- let { firstValue, secondValue, oldValue, inputValue, precision } = this.state;
+ const nextValue = state.draggingValue;
if (range) {
- if (Array.isArray(value)) {
- firstValue = Math.max(min, value[0]);
- secondValue = Math.min(max, value[1]);
+ if (Array.isArray(nextValue)) {
+ state.firstValue = Math.max(min, nextValue[0]);
+ state.secondValue = Math.min(max, nextValue[1]);
} else {
- firstValue = min;
- secondValue = max;
+ state.firstValue = min;
+ state.secondValue = max;
}
- oldValue = [firstValue, secondValue];
+ state.draggingValue = [state.firstValue, state.secondValue];
} else {
- firstValue = this.initValue;
- oldValue = firstValue;
+ state.firstValue = Slider.getInitValue(nextValue, props);
+ state.draggingValue = state.firstValue;
}
- let precisions = [min, max, step].map(item => {
- let decimal = ('' + item).split('.')[1];
+ const precisions = [min, max, step].map(item => {
+ const decimal = ('' + item).split('.')[1];
return decimal ? decimal.length : 0;
});
- precision = Math.max.apply(null, precisions);
- inputValue = inputValue || firstValue;
-
- this.setState({ firstValue, secondValue, oldValue, inputValue, precision });
- }
-
- componentWillUpdate(props: Object): void {
- const { min, max, value, range } = this.props;
- const { dragging } = this.state;
- if (props.min != min || props.max != max) {
- this.setValues();
- }
-
- if (props.value != value) {
- const { oldValue } = this.state;
+ state.precision = Math.max.apply(null, precisions);
+ state.inputValue = state.inputValue || state.firstValue;
- if (dragging || Array.isArray(value) && Array.isArray(props.value) && Array.isArray(oldValue) && value.every((item, index) => item === oldValue[index])) {
- return;
- } else if (!range && typeof props.value === 'number' && !isNaN(props.value)) {
- this.setState({ firstValue: props.value })
- }
- this.setValues();
- }
+ return state;
}
- valueChanged(): boolean {
- const { range } = this.props;
- const { firstValue, oldValue } = this.state;
- if (range && Array.isArray(oldValue)) {
- return ![this.minValue(), this.maxValue()].every((item, index) => item === oldValue[index]);
+ static getInitValue(value: Value, { min, max }: Object): number {
+ let initValue = value;
+ if (typeof value !== 'number' || isNaN(value)) {
+ initValue = min;
} else {
- return firstValue !== oldValue;
+ initValue = Math.min(max, Math.max(min, value));
}
+ return initValue;
}
+ state: State = {
+ firstValue: 0,
+ secondValue: 0,
+ draggingValue: this.props.value,
+ precision: 0,
+ inputValue: 0
+ };
+
+ slider: Function = React.createRef();
+ button1: Function = React.createRef();
+ button2: Function = React.createRef();
+
+ get dragging(): boolean {
+ return this.button1.current && this.button1.current.state.dragging
+ || this.button2.current && this.button2.current.state.dragging;
+ }
+ getChildContext(): Object {
+ return {
+ component: this
+ };
+ }
- setValues(): void {
- const { range, value, min, max } = this.props;
- let { firstValue, secondValue, oldValue, inputValue } = this.state;
+ setValues(state: Object): void {
+ const { range, min, max, triggerOnDragging = false, onChange } = this.props;
+ const states = { ...this.state, ...state };
+ const { firstValue, secondValue } = states;
+ let { inputValue } = states;
+ let changes;
- if (range && Array.isArray(value)) {
- if (value[1] < min) {
- inputValue = [min, min];
- } else if (value[0] > max) {
- inputValue = [max, max];
- } else if (value[0] < min) {
- inputValue = [min, value[1]];
- } else if (value[1] > max) {
- inputValue = [value[0], max];
+ if (range) {
+ if (secondValue < min) {
+ changes = [min, min];
+ } else if (firstValue > max) {
+ changes = [max, max];
+ } else if (firstValue < min) {
+ changes = [min, secondValue];
+ } else if (secondValue > max) {
+ changes = [firstValue, max];
} else {
- firstValue = value[0];
- secondValue = value[1];
-
- if (this.valueChanged()) {
- this.onValueChanged([this.minValue(), this.maxValue()]);
-
- oldValue = value.slice();
- }
+ changes = [Math.min(firstValue, secondValue), Math.max(firstValue, secondValue)];
}
- } else if (!range && typeof value === 'number' && !isNaN(value)) {
- if (this.initValue < min) {
- inputValue = min;
- } else if (this.initValue > max) {
- inputValue = max;
+ } else if (typeof firstValue === 'number' && !isNaN(firstValue)) {
+ const initValue = Slider.getInitValue(firstValue, this.props);
+ if (initValue < min) {
+ changes = min;
+ } else if (initValue > max) {
+ changes = max;
} else {
- inputValue = firstValue;
-
- this.setState({ firstValue }, () => {
- if (this.valueChanged()) {
- this.onValueChanged(firstValue);
- this.setState({ oldValue: firstValue });
- }
- });
+ changes = firstValue;
+ inputValue = changes;
}
}
- this.setState({ firstValue, secondValue, inputValue });
+ this.setState({ firstValue, secondValue, inputValue, draggingValue: changes });
+
+ if (typeof changes !== 'undefined' && onChange && !(Number(triggerOnDragging) ^ Number(this.dragging))) {
+ onChange(changes);
+ }
}
setPosition(percent: number): void {
@@ -161,24 +126,24 @@ export default class Slider extends Component {
const targetValue = min + percent * (max - min) / 100;
if (!range) {
- this.button1.current.setPosition(percent);
+ this.button1 && this.button1.current.setPosition(percent);
return;
}
let button;
- if (Math.abs(this.minValue() - targetValue) < Math.abs(this.maxValue() - targetValue)) {
+ if (Math.abs(Math.min(firstValue, secondValue) - targetValue) < Math.abs(Math.max(firstValue, secondValue) - targetValue)) {
button = firstValue < secondValue ? 'button1' : 'button2';
} else {
button = firstValue > secondValue ? 'button1' : 'button2';
}
- this[button].current.setPosition(percent);
+ this[button] && this[button].current.setPosition(percent);
}
onSliderClick(event: SyntheticMouseEvent): void {
- const { disabled, dragging, vertical } = this.props;
- if (disabled || dragging) return;
+ const { disabled, vertical } = this.props;
+ if (disabled || this.dragging || !this.slider.current) return;
if (vertical) {
const sliderOffsetBottom = this.slider.current.getBoundingClientRect().bottom;
@@ -188,48 +153,27 @@ export default class Slider extends Component {
this.setPosition((event.clientX - sliderOffsetLeft) / this.sliderSize() * 100);
}
- this.setValues();
+ this.setValues(this.state);
}
- /* Watched Methods */
- onValueChanged(val: number | Array): void {
- const { onChange } = this.props;
- if (onChange) onChange(val);
- }
-
- onInputValueChanged(e: number): void {
- this.setState({
- inputValue: e || 0,
- firstValue: e || 0
- }, () => {
- this.setValues();
+ onInputValueChanged(value: number): void {
+ this.setValues({
+ inputValue: value || 0,
+ firstValue: value || 0
});
}
- onFirstValueChange(value: number): void {
- const { firstValue } = this.state;
- if (firstValue !== value) {
- this.setState({ firstValue: value }, () => this.setValues());
- }
- }
-
- onSecondValueChange(value: number): void {
- const { secondValue } = this.state;
- if (secondValue !== value) {
- this.setState({ secondValue: value }, () => this.setValues());
- }
- }
-
/* Computed Methods */
sliderSize(): number {
const {vertical} = this.props;
+ if (!this.slider.current) return 0;
return parseInt(vertical ? this.slider.current.offsetHeight : this.slider.current.offsetWidth, 10);
}
stops(): Array {
const { range, min, max, step } = this.props;
- const { firstValue } = this.state;
+ const { firstValue, secondValue } = this.state;
const stopCount = (max - min) / step;
const stepWidth = 100 * step / (max - min);
@@ -241,24 +185,14 @@ export default class Slider extends Component {
if (range) {
return result.filter(step => {
- return step < 100 * (this.minValue() - min) / (max - min) ||
- step > 100 * (this.maxValue() - min) / (max - min);
+ return step < 100 * (Math.min(firstValue, secondValue) - min) / (max - min) ||
+ step > 100 * (Math.max(firstValue, secondValue) - min) / (max - min);
});
} else {
return result.filter(step => step > 100 * (firstValue - min) / (max - min));
}
}
- minValue(): number {
- const { firstValue, secondValue } = this.state;
- return Math.min(firstValue, secondValue);
- }
-
- maxValue(): number {
- const { firstValue, secondValue } = this.state;
- return Math.max(firstValue, secondValue);
- }
-
runwayStyle(): Object {
const { vertical, height } = this.props;
return vertical ? { height } : {};
@@ -278,17 +212,18 @@ export default class Slider extends Component {
}
barSize(): string {
- const { firstValue } = this.state;
+ const { firstValue, secondValue} = this.state;
const { range, max, min } = this.props;
return range
- ? `${100 * (this.maxValue() - this.minValue()) / (max - min)}%`
+ ? `${100 * (Math.max(firstValue, secondValue) - Math.min(firstValue, secondValue)) / (max - min)}%`
: `${100 * (firstValue - min) / (max - min)}%`;
}
barStart(): string {
+ const { firstValue, secondValue} = this.state;
const { range, max, min } = this.props;
return range
- ? `${100 * (this.minValue() - min) / (max - min)}%`
+ ? `${100 * (Math.min(firstValue, secondValue) - min) / (max - min)}%`
: '0%';
}
@@ -334,14 +269,14 @@ export default class Slider extends Component {
this.setValues({ firstValue: v })}
/>
{
range && (
this.setValues({ secondValue: v })}
/>
)
}
@@ -378,8 +313,9 @@ Slider.propTypes = {
vertical: PropTypes.bool,
height: PropTypes.string,
formatTooltip: PropTypes.func,
- onChange: PropTypes.func
-}
+ onChange: PropTypes.func,
+ triggerOnDragging: PropTypes.bool
+};
Slider.defaultProps = {
showTooltip: true,
@@ -387,5 +323,6 @@ Slider.defaultProps = {
min: 0,
max: 100,
step: 1,
- value: 0
-}
+ value: 0,
+ triggerOnDragging: false
+};