mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 14:33:22 +00:00
168 lines
4.4 KiB
JavaScript
168 lines
4.4 KiB
JavaScript
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { findDOMNode } from 'react-dom';
|
|
|
|
const MARGIN_LR = 8; // Left/ Right margin
|
|
|
|
/**
|
|
* Horizontal Slider
|
|
*/
|
|
export default class Slider extends React.Component {
|
|
|
|
static defaultProps = {
|
|
axis: false,
|
|
min: 0,
|
|
max: 1,
|
|
scale: 1 // SVG render scale
|
|
};
|
|
|
|
static propTypes = {
|
|
axis: PropTypes.bool,
|
|
axisUnit: PropTypes.string,
|
|
max: PropTypes.number,
|
|
min: PropTypes.number,
|
|
onChange: PropTypes.func.isRequired,
|
|
onResize: PropTypes.func,
|
|
percent: PropTypes.number.isRequired,
|
|
scale: PropTypes.number
|
|
};
|
|
|
|
/**
|
|
* Constructor
|
|
* @param {Object} props React Component properties
|
|
*/
|
|
constructor(props) {
|
|
super(props);
|
|
this._down = this._down.bind(this);
|
|
this._move = this._move.bind(this);
|
|
this._up = this._up.bind(this);
|
|
this._updatePercent = this._updatePercent.bind(this);
|
|
this._updateDimensions = this._updateDimensions.bind(this);
|
|
|
|
this.state = { width: 0 };
|
|
}
|
|
|
|
/**
|
|
* On Mouse/Touch down handler
|
|
* @param {SyntheticEvent} event Event
|
|
*/
|
|
_down(event) {
|
|
let rect = event.currentTarget.getBoundingClientRect();
|
|
this.left = rect.left;
|
|
this.width = rect.width;
|
|
this._move(event);
|
|
}
|
|
|
|
/**
|
|
* Update the slider percentage on move
|
|
* @param {SyntheticEvent} event Event
|
|
*/
|
|
_move(event) {
|
|
if(this.width !== null && this.left != null) {
|
|
let clientX = event.touches ? event.touches[0].clientX : event.clientX;
|
|
event.preventDefault();
|
|
this._updatePercent(clientX - this.left, this.width);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* On Mouse/Touch up handler
|
|
* @param {Event} event DOM Event
|
|
*/
|
|
_up(event) {
|
|
event.preventDefault();
|
|
this.left = null;
|
|
this.width = null;
|
|
}
|
|
|
|
/**
|
|
* Determine if the user is still dragging
|
|
* @param {SyntheticEvent} event Event
|
|
*/
|
|
_enter(event) {
|
|
if(event.buttons !== 1) {
|
|
this.left = null;
|
|
this.width = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the slider percentage
|
|
* @param {number} pos Slider drag position
|
|
* @param {number} width Slider width
|
|
* @param {Event} event DOM Event
|
|
*/
|
|
_updatePercent(pos, width) {
|
|
this.props.onChange(Math.min(Math.max(pos / width, 0), 1));
|
|
}
|
|
|
|
/**
|
|
* Update dimenions from rendered DOM
|
|
*/
|
|
_updateDimensions() {
|
|
this.setState({
|
|
outerWidth: findDOMNode(this).getBoundingClientRect().width
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Add listeners when about to mount
|
|
*/
|
|
componentWillMount() {
|
|
if (this.props.onResize) {
|
|
this.resizeListener = this.props.onResize(this._updateDimensions);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trigger DOM updates on mount
|
|
*/
|
|
componentDidMount() {
|
|
this._updateDimensions();
|
|
}
|
|
|
|
/**
|
|
* Remove listeners on unmount
|
|
*/
|
|
componentWillUnmount() {
|
|
if (this.resizeListener) {
|
|
this.resizeListener.remove();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render the slider
|
|
* @return {React.Component} The slider
|
|
*/
|
|
render() {
|
|
let outerWidth = this.state.outerWidth;
|
|
let { axis, axisUnit, min, max, scale } = this.props;
|
|
|
|
let style = {
|
|
width: '100%',
|
|
height: axis ? '2.5em' : '1.5em',
|
|
boxSizing: 'border-box'
|
|
};
|
|
|
|
if (!outerWidth) {
|
|
return <svg style={style} />;
|
|
}
|
|
|
|
let margin = MARGIN_LR * scale;
|
|
let width = outerWidth - (margin * 2);
|
|
let pctPos = width * this.props.percent;
|
|
|
|
return <svg onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onTouchEnd={this._up} style={style}>
|
|
<rect className='primary' style={{ opacity: 0.3 }} x={margin} y='0.25em' rx='0.3em' ry='0.3em' width={width} height='0.7em' />
|
|
<rect className='primary-disabled' x={margin} y='0.45em' rx='0.15em' ry='0.15em' width={pctPos} height='0.3em' />
|
|
<circle className='primary' r={margin} cy='0.6em' cx={pctPos + margin} />
|
|
<rect x={margin} width={width} height='100%' fillOpacity='0' style={{ cursor: 'col-resize' }} onMouseDown={this._down} onTouchMove={this._move} onTouchStart={this._down} />
|
|
{axis && <g style={{ fontSize: '.7em' }}>
|
|
<text className='primary-disabled' y='3em' x={margin} style={{ textAnchor: 'middle' }}>{min + axisUnit}</text>
|
|
<text className='primary-disabled' y='3em' x='50%' style={{ textAnchor: 'middle' }}>{(min + max / 2) + axisUnit}</text>
|
|
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
|
|
</g>}
|
|
</svg>;
|
|
}
|
|
}
|