2014-11-09 18:12:03 +00:00
|
|
|
/*jshint browser:true */
|
2014-11-08 17:52:36 +00:00
|
|
|
/*globals React, App */
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Display a scrollbar
|
|
|
|
*
|
|
|
|
* @prop {current: int} Current position
|
|
|
|
* @prop {max: int} Maximum number we can reach
|
|
|
|
* @prop {onChange: function} Called on scrolling attempt
|
|
|
|
* @prop {onDragStart: function} Called on start of scrolling
|
|
|
|
* @prop {onDragEnd: function} Called on end of scrolling
|
|
|
|
*/
|
|
|
|
App.components.create('UI', {
|
|
|
|
displayName: 'Scroll',
|
|
|
|
mixins: [React.addons.PureRenderMixin],
|
|
|
|
position: null,
|
|
|
|
dragging: false,
|
|
|
|
|
|
|
|
propTypes: {
|
|
|
|
current: React.PropTypes.number,
|
|
|
|
max: React.PropTypes.number,
|
|
|
|
onChange: React.PropTypes.func,
|
|
|
|
onDragStart: React.PropTypes.func,
|
|
|
|
onDragEnd: React.PropTypes.func
|
|
|
|
},
|
|
|
|
|
|
|
|
getDefaultProps: function () {
|
|
|
|
return {
|
|
|
|
current: 0,
|
|
|
|
max: 0,
|
|
|
|
onChange: function () {},
|
|
|
|
onDragStart: function () {},
|
|
|
|
onDragEnd: function () {}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
componentDidMount: function () {
|
|
|
|
window.addEventListener('mousemove', this.mouseMove);
|
|
|
|
window.addEventListener('mouseup', this.mouseUp);
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillUnmount: function () {
|
|
|
|
window.removeEventListener('mousemove', this.mouseMove);
|
|
|
|
window.removeEventListener('mouseup', this.mouseUp);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert distance to value
|
|
|
|
*/
|
|
|
|
convert: function (input) {
|
|
|
|
return input * this.props.max / this.getDOMNode().clientHeight;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert value to distance
|
|
|
|
*/
|
|
|
|
reverseConvert: function (output) {
|
|
|
|
return output * this.getDOMNode().clientHeight / this.props.max;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scroll by given position
|
|
|
|
*
|
|
|
|
* @param {position: int} Current position
|
|
|
|
*/
|
|
|
|
scrollBy: function (position) {
|
|
|
|
var current = this.props.max - this.props.current,
|
|
|
|
delta = this.convert(position - this.reverseConvert(current));
|
|
|
|
|
|
|
|
this.props.onChange(delta);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start scroll dragging
|
|
|
|
*/
|
|
|
|
mouseDown: function (e) {
|
|
|
|
if (e.button !== 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.position = e.clientY;
|
|
|
|
this.props.onDragStart();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move scroll
|
|
|
|
*/
|
|
|
|
mouseMove: function (e) {
|
|
|
|
if (this.position === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Math.abs(e.clientY - this.position) >= 3) {
|
|
|
|
this.dragging = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.dragging) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.scrollBy(e.clientY);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mouse release
|
|
|
|
*/
|
|
|
|
mouseUp: function (e) {
|
|
|
|
if (this.dragging) {
|
|
|
|
this.props.onDragEnd();
|
|
|
|
|
|
|
|
this.dragging = false;
|
|
|
|
this.position = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Click on track
|
|
|
|
*/
|
|
|
|
click: function (e) {
|
|
|
|
if (e.button === 0) {
|
|
|
|
this.scrollBy(e.clientY);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render scroll
|
|
|
|
*/
|
|
|
|
render: function () {
|
|
|
|
var bottom;
|
|
|
|
|
|
|
|
if (this.props.max === 0) {
|
|
|
|
bottom = '-100%';
|
|
|
|
} else {
|
|
|
|
bottom = 'calc(100% / ' + this.props.max + ' * ' +
|
|
|
|
this.props.current + ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
return React.DOM.div({
|
|
|
|
className: 'scroll',
|
|
|
|
onClick: this.click
|
|
|
|
}, [
|
|
|
|
React.DOM.span({
|
|
|
|
key: 'position',
|
|
|
|
className: 'position',
|
|
|
|
onMouseDown: this.mouseDown,
|
|
|
|
|
|
|
|
style: {
|
|
|
|
bottom: bottom
|
|
|
|
}
|
|
|
|
})
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}());
|