156 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /*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
 | |
|                     }
 | |
|                 })
 | |
|             ]);
 | |
|         }
 | |
|     });
 | |
| }()); |