@ustack/uskin
Version:
A graceful framework which provides developers another chance to build an amazing site.
188 lines (153 loc) • 4.15 kB
JSX
import PropTypes from 'prop-types';
import React from 'react';
import styles from '../../mixins/styles';
function noop() {}
class Slider extends React.Component {
constructor(props) {
super(props);
this.state = {
perc: 0,
value: 0
};
['getProps', 'startSlide', 'endSlide', 'updateSlide', 'finishSlide',
'disableAnimate', 'enableAnimate'].forEach((func) => {
this[func] = this[func].bind(this);
});
}
componentWillMount() {
this.getProps(this.props);
}
componentWillReceiveProps(nextProps) {
this.getProps(nextProps);
}
getProps(props) {
this._step = props.step <= 0 ? -props.step : props.step;
let unit = (this._step).toString().split('.')[1];
this._unit = unit ? unit.length : 0;
this._min = props.min;
this._max = props.max;
this._sum = this._max - this._min;
let value = props.value;
if (value < this._min) {
value = this._min;
} else if (value > this._max) {
value = this._max;
}
this.setState({
perc: (value - this._min) / this._sum,
value: value
});
}
startSlide(e) {
e.preventDefault();
document.addEventListener('mousemove', this.updateSlide, false);
document.addEventListener('mouseup', this.endSlide, false);
}
disableAnimate() {
this.refs.track.classList.remove('animate');
this.refs.thumb.classList.remove('animate');
}
enableAnimate() {
this.refs.track.classList.add('animate');
this.refs.thumb.classList.add('animate');
}
endSlide(e) {
document.removeEventListener('mousemove', this.updateSlide, false);
document.removeEventListener('mouseup', this.endSlide, false);
this.enableAnimate();
this.finishSlide(e);
}
inbound(perc) {
if (perc < 0) {
return 0;
} else if (perc > 1) {
return 1;
} else {
return perc;
}
}
trackOffsetPerc(e) {
return this.inbound(
(e.clientX - this.refs.slider.getBoundingClientRect().left) / this.refs.slider.offsetWidth
);
}
trackOffsetCloserPerc(e) {
let perc = this.trackOffsetPerc(e);
let percStep = 1 / (this._sum / this._step);
return this.inbound(Math.round(perc / percStep) * percStep);
}
trackOffsetValue(perc) {
return (parseFloat(this._sum * perc) + parseFloat(this._min)).toFixed(this._unit);
}
updateSlide(e) {
this.disableAnimate();
let perc = this.trackOffsetPerc(e);
let value = this.trackOffsetValue(perc);
this.setState({
perc: perc,
value: value
});
this.props.onChange(e, value);
}
finishSlide(e) {
this.enableAnimate();
let perc = this.trackOffsetCloserPerc(e);
let value = this.trackOffsetValue(perc);
this.setState({
perc: perc,
value: value
});
this.props.onChange(e, value);
}
render() {
const props = this.props;
const state = this.state;
const min = this._min;
const max = this._max;
const {disabled, hideThumb} = props;
const style = styles.getWidth(props.width);
let className = 'slider';
if (disabled) {
className += ' disabled';
} else if (hideThumb) {
className += ' noclick';
}
return (
<div className={className}
data-min={min}
data-max={max}
data-value={state.value}
ref="slider"
onMouseDown={disabled || hideThumb ? null : this.startSlide}
style={{width: style.width}}>
<div ref="track"
className="slider-track"
style={{width: (state.perc * 100) + '%'}} />
<div ref="thumb"
className={'slider-thumb' + (props.hideThumb ? ' hide' : '')}
style={{left: (state.perc * 100) + '%'}} />
</div>
);
}
}
Slider.propTypes = {
min: PropTypes.number,
max: PropTypes.number,
step: PropTypes.number,
value: PropTypes.number,
width: PropTypes.number,
onChange: PropTypes.func,
hideThumb: PropTypes.bool,
disabled: PropTypes.bool
};
Slider.defaultProps = {
min: 0,
max: 100,
step: 1,
value: 0,
width: 300,
onChange: noop,
hideThumb: false,
disabled: false
};
export default Slider;