UNPKG

labo-components

Version:
182 lines (160 loc) 5.49 kB
import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import IDUtil from '../../../util/IDUtil'; export default class TimeInput extends React.PureComponent { constructor(props) { super(props); this.hours = React.createRef(); this.minutes = React.createRef(); this.seconds = React.createRef(); this.state = this.parseTime(this.props.time); } parseTime(time) { const hours = Math.floor(time / 3600); time -= hours * 3600; const minutes = Math.floor(time / 60); time -= minutes * 60; const seconds = Math.floor(time); time -= seconds; return { hours, minutes, seconds }; } getTime(hours, minutes, seconds) { return Math.max(0, hours * 3600 + minutes * 60 + seconds); } onUpdate = () => { const values = this.getFormValues(); const time = this.getTime(values.hours, values.minutes, values.seconds); this.setState(this.parseTime(time), () => { // always force update to prevent negative inputs this.forceUpdate(); }); // optional callback if (this.props.onUpdate) { this.props.onUpdate(time); } }; // can be called from ref getValue() { const values = this.getFormValues(); return this.getTime(values.hours, values.minutes, values.seconds); } // can be called from ref setValue(time) { this.setState(this.parseTime(time)); } getFormValues = () => { // additional checks to make the value increase/decrease, but not zero out smaller values // hours let hours = Math.max(0, parseInt(this.hours.current.value)); hours = isNaN(hours) ? 0 : hours; // minutes let minutes = parseInt(this.minutes.current.value.toString().slice(-2)); minutes = isNaN(minutes) ? 0 : minutes; if (hours == 0) { minutes = Math.max(0, minutes); } // seconds let seconds = parseInt(this.seconds.current.value.toString().slice(-2)); seconds = isNaN(seconds) ? 0 : seconds; if (minutes == 0) { seconds = Math.max(0, seconds); } return { hours, minutes, seconds }; }; zeroPad(v) { const zero = 2 - v.toString().length + 1; return Array(+(zero > 0 && zero)).join('0') + v; } onFocus = e => { e.target.select(); }; onKeyDown = e => { switch (e.keyCode) { case 38: // up e.target.value = parseInt(e.target.value) + (e.shiftKey ? 10 : 1); this.onUpdate(); break; case 40: // down e.target.value = parseInt(e.target.value) - (e.shiftKey ? 10 : 1); this.onUpdate(); break; case 13: // enter this.onFinish(); break; } }; onBlur = () => { this.forceUpdate(); }; // optional onFinish callback will be called on blur or on enter-key // to let parent know input can be progressed onFinish = () => { if (this.props.onFinish) { const values = this.getFormValues(); const time = this.getTime( values.hours, values.minutes, values.seconds ); this.props.onFinish(time); } }; render() { return ( <div className={classNames(IDUtil.cssClassName('time-input'))}> <input type="text" ref={this.hours} value={ this.hours.current == document.activeElement ? this.state.hours : this.zeroPad(this.state.hours) } onChange={this.onUpdate} onFocus={this.onFocus} onKeyDown={this.onKeyDown} onBlur={this.onBlur} /> : <input type="text" ref={this.minutes} value={ this.minutes.current == document.activeElement ? this.state.minutes : this.zeroPad(this.state.minutes) } maxLength={2} onChange={this.onUpdate} onFocus={this.onFocus} onKeyDown={this.onKeyDown} onBlur={this.onBlur} /> : <input type="text" ref={this.seconds} value={ this.seconds.current == document.activeElement ? this.state.seconds : this.zeroPad(this.state.seconds) } maxLength={2} onChange={this.onUpdate} onFocus={this.onFocus} onKeyDown={this.onKeyDown} onBlur={this.onBlur} /> </div> ); } } TimeInput.propTypes = { // time in seconds time: PropTypes.number.isRequired, onUpdate: PropTypes.func, onFinish: PropTypes.func };