@bigfishtv/cockpit
Version:
217 lines (194 loc) • 5.46 kB
JavaScript
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import DatePicker from 'react-datepicker'
import moment from 'moment'
import Icon from '../Icon'
const valueDateTimeFormat = 'YYYY-MM-DDTHH:mm:ssZ'
const dateFormat = 'DD/MM/YY'
const timeFormat = 'h:mm A'
const dateFormats = [
// day/month/year
'DD/MM/YYYY',
'D/MM/YYYY',
'DD/M/YYYY',
'D/M/YYYY',
'DD/MM/YY',
'D/M/YY',
'D/MM/YY',
'DD/M/YY',
// year-month-day
'YYYY-MM-DD',
'YYYY-MM-D',
'YYYY-M-DD',
'YYYY-M-D',
'YY-MM-DD',
'YY-MM-D',
'YY-M-DD',
'YY-M-D',
]
const timeFormats = ['', 'ha', 'h:mm', 'h:mma', 'h', 'hh']
export default class DateTimeInput extends Component {
static propTypes = {
value: PropTypes.string,
defaultTime: PropTypes.string,
}
static defaultProps = {
displayTime: true,
defaultTime: '',
onChange: value => console.warn('No onChange prop set for DateTime'),
readOnly: false,
}
constructor(props) {
super()
const state = this.getStateForValue(props)
this.state = {
dirtyTime: false,
dirtyDate: false,
dateValid: state.dateValue !== '',
timeValid: state.timeValue !== '',
...state,
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.value != this.props.value && !this.state.dirty) {
this.setState({ ...this.getStateForValue(nextProps) })
}
}
getStateForValue(nextProps) {
const dateTimeString = nextProps.value
if (!dateTimeString) {
return {
dateValue: '',
timeValue: nextProps.defaultTime,
currentDate: null,
}
}
const dateTime = moment(dateTimeString, valueDateTimeFormat)
return {
dateValue: dateTime.format(dateFormat),
timeValue: dateTime.hour() == 0 && dateTime.minute() == 0 ? '' : dateTime.format(timeFormat),
currentDate: dateTime,
}
}
handleChangeDate = event => {
// if parsed + formatted date is the same as the
// input value, then just send an onChange event
// TODO: also support this when defaultTime is provided
const date = moment(event.target.value, dateFormats)
if (date.format(dateFormat) === event.target.value && !this.props.defaultTime) {
this.props.onChange(date.format(valueDateTimeFormat))
return
}
this.setState({
dirtyDate: true,
dateValue: event.target.value,
})
}
handleChangeDatepicker = dateTime => {
this.setState(
{
dateValue: dateTime.format(dateFormat),
dirtyDate: true,
timeValue: this.state.timeValue || this.props.defaultTime,
currentDate: dateTime,
},
() => this.handleBlur()
)
setTimeout(() => this.dateInputRef.focus(), 1)
}
handleChangeTime = event => {
// if parsed + formatted time is the same as the
// input value, then just send an onChange event
const time = moment(event.target.value, timeFormats)
if (time.format(timeFormat) === event.target.value) {
const date = moment(this.state.dateValue, dateFormats)
date.hours(time.hours())
date.minutes(time.minutes())
date.seconds(time.seconds())
this.props.onChange(date.format(valueDateTimeFormat))
return
}
this.setState({
dirtyTime: true,
timeValue: event.target.value,
})
}
handleBlur = event => {
const { dateValue, timeValue, dirtyDate, dirtyTime } = this.state
// if no values have changed then donna worry bout ittt
if (!dirtyTime && !dirtyDate) return
// attempt to parse date and time from input values
const date = moment(dateValue, dateFormats)
const time = moment(timeValue, timeFormats)
// update state, including if values are valid
this.setState({ dirtyDate: false, dirtyTime: false, dateValid: date.isValid(), timeValid: time.isValid() }, () => {
// return blank date if invalid
if (!this.state.dateValid) {
this.props.onChange(null)
return
}
// copy time value into date object
if (this.state.timeValid) {
date.hours(time.hours())
date.minutes(time.minutes())
date.seconds(time.seconds())
}
this.props.onChange(date.format(valueDateTimeFormat))
})
}
handleKeyDown = event => {
if (event.key === 'Enter') {
this.handleBlur()
}
}
render() {
const { timeValue, dateValue } = this.state
const { value, minDate, maxDate, displayTime } = this.props
return (
<div className="inputs-inline">
<div className="input-group">
<div className="input-group-icon">
{!this.props.readOnly && !this.props.disabled && (
<DatePicker
showYearDropdown
selected={this.state.currentDate}
onChange={this.handleChangeDatepicker}
minDate={minDate ? moment(minDate) : null}
maxDate={maxDate ? moment(maxDate) : null}
tabIndex={-1}
/>
)}
<Icon name="calendar" size="18" />
</div>
<input
ref={el => (this.dateInputRef = el)}
type="text"
placeholder={dateFormat}
value={dateValue}
onChange={this.handleChangeDate}
onBlur={this.handleBlur}
onKeyDown={this.handleKeyDown}
readOnly={this.props.readOnly}
autoFocus={this.props.autoFocus}
/>
</div>
{displayTime && value && (
<div className="input-group">
<div className="input-group-icon">
<Icon name="clock" size="18" />
</div>
<input
type="text"
placeholder={value ? '12:00 AM' : ''}
value={timeValue}
onChange={this.handleChangeTime}
onBlur={this.handleBlur}
onKeyDown={this.handleKeyDown}
readOnly={this.props.readOnly}
/>
</div>
)}
</div>
)
}
}