diff options
Diffstat (limited to 'app/components/Switch.js')
| -rw-r--r-- | app/components/Switch.js | 89 |
1 files changed, 58 insertions, 31 deletions
diff --git a/app/components/Switch.js b/app/components/Switch.js index 4cb0ea34c3..46baeb8b22 100644 --- a/app/components/Switch.js +++ b/app/components/Switch.js @@ -1,27 +1,32 @@ +// @flow import React, { Component } from 'react'; -import PropTypes from 'prop-types'; + +import type { Point2d } from '../types'; const CLICK_TIMEOUT = 1000; const MOVE_THRESHOLD = 10; export default class Switch extends Component { + props: { + isOn: boolean; + onChange: ?((isOn: boolean) => void); + } - static propTypes = { - isOn: PropTypes.bool, - onChange: PropTypes.func + defaultProps = { + isOn: false } - constructor(props) { - super(props); - this.state = { - isTracking: false, - ignoreChange: false, - initialPos: null, - startTime: null - }; + ref: ?HTMLInputElement; + onRef = (e: HTMLInputElement) => this.ref = e; + + state = { + isTracking: false, + ignoreChange: false, + initialPos: (null: ?Point2d), + startTime: (null: ?number) } - handleMouseDown(e) { + handleMouseDown = (e: MouseEvent) => { const { pageX: x, pageY: y } = e; this.setState({ isTracking: true, @@ -30,14 +35,19 @@ export default class Switch extends Component { }); } - handleMouseMove(e) { - if(!this.state.isTracking) { return; } + handleMouseMove = (e: MouseEvent) => { + if(!this.state.isTracking) { + return; + } + const inputElement = this.ref; const { x: x0 } = this.state.initialPos; const { pageX: x, pageY: y } = e; const dx = Math.abs(x0 - x); - if(dx < MOVE_THRESHOLD) { return; } + if(dx < MOVE_THRESHOLD) { + return; + } const isOn = !!this.props.isOn; let nextOn = isOn; @@ -53,12 +63,16 @@ export default class Switch extends Component { initialPos: { x, y }, ignoreChange: true }); - this.refs.input.checked = nextOn; + + if(inputElement) { + inputElement.checked = nextOn; + } + this.notify(nextOn); } } - handleMouseUp() { + handleMouseUp = () => { if(this.state.isTracking) { this.setState({ isTracking: false, @@ -67,8 +81,19 @@ export default class Switch extends Component { } } - handleChange(e) { - const dt = e.timeStamp - this.state.startTime; + handleChange = (e: Event) => { + const startTime = this.state.startTime; + const eventTarget = e.target; + + if(typeof(startTime) !== 'number') { + throw new Error('startTime must be a number.'); + } + + if(!(eventTarget instanceof HTMLInputElement)) { + throw new Error('e.target must be an instance of HTMLInputElement.'); + } + + const dt = e.timeStamp - startTime; if(this.state.ignoreChange) { this.setState({ ignoreChange: false }); @@ -76,30 +101,32 @@ export default class Switch extends Component { } else if(dt > CLICK_TIMEOUT) { e.preventDefault(); } else { - this.notify(e.target.checked); + this.notify(eventTarget.checked); } } - notify(isOn) { - if(this.props.onChange) { - this.props.onChange(isOn); + notify(isOn: boolean) { + const onChange = this.props.onChange; + if(onChange) { + onChange(isOn); } } componentDidMount() { - document.addEventListener('mousemove', ::this.handleMouseMove); - document.addEventListener('mouseup', ::this.handleMouseUp); + document.addEventListener('mousemove', this.handleMouseMove); + document.addEventListener('mouseup', this.handleMouseUp); } componentWillUnmount() { - document.removeEventListener('mousemove', ::this.handleMouseMove); - document.removeEventListener('mouseup', ::this.handleMouseUp); + document.removeEventListener('mousemove', this.handleMouseMove); + document.removeEventListener('mouseup', this.handleMouseUp); } - render() { + render(): React.Element<*> { return ( - <input type="checkbox" ref="input" className="switch" checked={ this.props.isOn } - onMouseDown={ ::this.handleMouseDown } onChange={ ::this.handleChange } /> + <input type="checkbox" ref={ this.onRef } className="switch" checked={ this.props.isOn } + onMouseDown={ this.handleMouseDown } + onChange={ this.handleChange } /> ); } } |
