import React,{PropTypes} from 'react';
import Component from './utils/Component';
import classnames from 'classnames';
import {setPhoenixPrefix} from './utils/Tool';
import Drag from './Drag';
/**
* 滑动输入条组件<br/>
* - 滑动进度条确定当前进度的百分比。
* - 可通过设置process确定初始进度百分比, 范围从0-100。
* - 可通过placement设置当前进度提示框的位置, 可选top/bottoom。
* - 可通过tipStay设置初始和松开按钮时提示是否消失,默认false不显示。
* - 可通过range制定范围,默认0-100,必需是长度为2的数组,第一个数字表示初始,第二个数字表示终点。
* - 可通过showRange判断是否在进度条前后显示范围,默认不显示。
* - 可通过duration设置固定移动的距离,默认1。
* - 可通过onSliderChange设置拖拽进度条松开时的回调函数。
* - 可通过disabled设置进度条只读。
* - 使用Slider前确保父级是有宽度的元素;使用flex需要加一层宽度100%的外壳。
*
* 主要属性和接口:
* - process:初始进度百分比, 默认0 <br/>
* 如: `<Slider progress={10}/>`
* - placement:进度提示框的位置, 默认top <br/>
* 如: `<Slider placement="bottom" />`
* - tipStay:初始和松开按钮时提示是否消失,默认false <br/>
* 如: `<Slider tipStay />`
* - range:范围,默认[0,100]。 <br/>
* 如: `<Slider range={[20,50]} />`
* - showRange:是否在进度条前后显示范围,默认不显示。 <br/>
* 如: `<Slider showRange />`
* - duration:固定移动的距离,默认1。 <br/>
* 如: `<Slider duration={20} />`
* - onSliderChange:拖拽进度条松开时的回调函数 <br/>
* 如: `<Slider onSliderChange={(progress)=>{console.log(progress);} />`
* - disabled:进度条只读, 不可操作 <br/>
* 如: `<Slider disabled/>`
*
* @class Slider
* @module 操作类组件
* @extends Component
* @constructor
* @since 1.0.0
* @demo slider|slider.js {展示}
* @show true
* */
export default class Slider extends Component{
static propTypes = {
/**
* 样式前缀
* @property classPrefix
* @type String
* @default 'slider'
* */
classPrefix: PropTypes.string,
/**
* 标签tagName
* @property componentTag
* @type String
* */
componentTag:PropTypes.string,
/**
* 初始进程,默认0
* @property progress
* @type String
* */
progress:PropTypes.number,
/**
* 进程提示的位置,默认top
* @property placement
* @type String
* @default 'top'
* */
placement: PropTypes.string,
/**
* 范围,默认0-100,可传固定范围的数组如:[25,50]
* @property range
* @type Array
* @default [0,100]
* */
range: PropTypes.array,
/**
* 是否在进度条前后显示范围
* @property showRange
* @type Boolean
* @default false
* */
showRange: PropTypes.bool,
/**
* 每次移动的固定距离,默认1
* @property duration
* @type Number
* @default 1
* */
duration: PropTypes.number,
/**
* 初始及松开按钮时是否显示进度
* @property tipStay
* @type Boolean
* @default false
* */
tipStay: PropTypes.bool,
/**
* 改变进程时的回调函数
* @method onSliderChange
* @type Function
* */
onSliderChange: PropTypes.func
};
static defaultProps = {
placement: 'top',
progress: 0,
range: [0,100],
showRange: false,
duration: 1,
classPrefix:'slider',
componentTag:'div',
classMapping : {
'disabled': 'disabled',
'top': 'tip-top',
'bottom': 'tip-bottom'
}
};
constructor(props, context) {
super(props, context);
this.range = this.validateRange();
this.rangeDiff = this.range[1]-this.range[0];
this.duration = this.validateDuration();
this.eachDur = (this.range[1]-this.range[0])/this.duration;
this.state = {
realProgress: props.progress || this.range[0],
tipVisible: props.tipStay || false
}
}
validateRange(){
let {range} = this.props, defaultRange = [0,100];
if(!range instanceof Array) return defaultRange;
if(range.length != 2){
console.error('Invalid prop `range` of length not equal to 2.');
return defaultRange;
}
if(range[0] >= range[1]){
console.error('Invalid prop `range[0]` must be less than or equal to `range[1]`.');
return defaultRange;
}
return range;
}
validateDuration(){
let {duration} = this.props, defaultDuration = 1;
if(duration<=0) {
console.error('Invalid prop `duration` have to be Positive.');
return defaultDuration;
}
if((this.range[1] - this.range[0])%duration != 0){ // 不能整除的情况
console.error('Prop `duration` can not be divided by `range`.');
return defaultDuration;
}
return duration;
}
componentDidMount(){
this.sliderLength = parseInt(this.sliderLine.offsetWidth);
this.eachSection = this.sliderLength/this.rangeDiff*this.duration;
// if(this.eachSection<1) this.eachSection = 1; // 最小1px
this.newProgressWidth = this.getNewProgressWidth(this.state.realProgress);
this.setSliderPosition(this.newProgressWidth + 'px');
}
componentWillReceiveProps(nextProps){
if(this.state.realProgress != nextProps.progress){
this.setState({
realProgress: nextProps.progress
});
this.newProgressWidth = this.getNewProgressWidth(nextProps.progress);
this.setSliderPosition(this.newProgressWidth + 'px');
}
}
getNewProgressWidth(realProgress){ // 保留2位小数
return this.sliderLength * (Math.round((realProgress-this.range[0])/this.rangeDiff*100)/100);
}
setSliderPosition(distance){
this.sliderProgress.style.width = distance;
this.sliderBtn.style.left = distance;
}
onDrag(event, position){
let newProgress, nowSec;
this.preX = position.start.x;
this.X = position.move.x;
this.distance = this.X - this.preX;
this.prevProgressWidth = this.newProgressWidth + this.distance;
if(this.prevProgressWidth <= 0) this.prevProgressWidth = 0;
if(this.prevProgressWidth >= this.sliderLength) this.prevProgressWidth = this.sliderLength;
nowSec = Math.round(this.prevProgressWidth/this.eachSection, 0);
this.prevProgressWidth = this.eachSection*nowSec;
newProgress = this.prevProgressWidth/this.sliderLength * this.rangeDiff + this.range[0];
this.setSliderPosition(this.prevProgressWidth + 'px');
this.setState({
tipVisible: true,
realProgress: parseInt(newProgress)
});
}
onDrop(event, position){
if(!this.props.tipStay){
this.setState({
tipVisible: false
});
}
this.newProgressWidth = this.prevProgressWidth;
if(this.props.onSliderChange) this.props.onSliderChange(this.state.realProgress);
}
renderSliderRange(){
if(this.props.showRange){
return (
<div>
<strong className={setPhoenixPrefix("slider-range-start")}>{this.range[0]}</strong>
<strong className={setPhoenixPrefix("slider-range-end")}>{this.range[1]}</strong>
</div>
);
}else{
return '';
}
}
render(){
let {componentTag:Component, className, showRange} = this.props;
return (
<Component {...this.props} className={classnames(
this.getProperty(true),
className,
showRange? setPhoenixPrefix('keep-range'):''
)}>
{this.renderSliderRange()}
<div className={setPhoenixPrefix("slider-line")} ref={(sliderLine)=>{this.sliderLine=sliderLine}}>
<div className={setPhoenixPrefix("slider-progress")} ref={(sliderProgress)=>{this.sliderProgress=sliderProgress}}></div>
<div className={setPhoenixPrefix("slider-content")} ref={(sliderBtn)=>{this.sliderBtn=sliderBtn}}>
<div className={classnames(setPhoenixPrefix("slider-tip"), this.state.tipVisible?'show':'hide')}>{this.state.realProgress}</div>
<Drag className={setPhoenixPrefix("slider-btn")} onDrag={::this.onDrag} onDrop={::this.onDrop}></Drag>
</div>
</div>
</Component>
);
}
}