Created
February 8, 2017 15:54
-
-
Save luggage66/e30281df130ff734f8d27cb49ed086d6 to your computer and use it in GitHub Desktop.
A simple date input
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
import moment from 'moment'; | |
const DEFAULT_DISPLAY_FORMAT = 'MM/DD/YYYY'; // what the input shows when the user isn't typing | |
const DEFAULT_DATA_FORMAT = 'YYYY-MM-DD'; // the value is expected to be in this format and this control ouputs this format | |
const DEFAULT_ACCEPTED_FORMATS = [ //the user may type of of these | |
'MM/DD/YYYY', | |
'M/D/YY', | |
'M/D', | |
'MM-DD-YYYY', | |
'M-D-YY', | |
'M-D' | |
]; | |
export default class DateInput extends Component | |
{ | |
static propTypes = { | |
value: React.PropTypes.string, | |
onChange: React.PropTypes.func.isRequired, | |
onBlur: React.PropTypes.func, | |
dataFormat: React.PropTypes.string, //a moment.js format string | |
displayFormat: React.PropTypes.string, //a moment.js format string | |
acceptedFormats: React.PropTypes.oneOfType([ // moment.js format string, or an array of | |
React.PropTypes.string, | |
React.PropTypes.array | |
]) | |
} | |
constructor(props) { | |
super(props); | |
this.state = { | |
value: this.formatValue(props.value) | |
}; | |
//bindy bindy | |
[ | |
'handleBlur', | |
'handleChange' | |
].forEach(fnName => this[fnName] = this[fnName].bind(this)); | |
} | |
get dataFormat() { | |
return this.props.dataFormat || DEFAULT_DATA_FORMAT; | |
} | |
get displayFormat() { | |
return this.props.displayFormat || DEFAULT_DISPLAY_FORMAT; | |
} | |
get acceptedFormats() { | |
return this.props.acceptedFormats || DEFAULT_ACCEPTED_FORMATS; | |
} | |
// either fires onChange OR discards the current value. | |
fireChangeEvent(newValue) { | |
if (!this.props.onChange) return; | |
if (moment.isMoment(newValue)) { //we are a date | |
if (newValue.isValid() && !newValue.isSame(moment(this.props.value, this.dataFormat))) { | |
// if we are a valid date AND different, then inform our parent | |
this.props.onChange(newValue.format(this.dataFormat)); | |
} | |
else { | |
// otherwise, just clean up. | |
this.discardValue(); | |
} | |
} | |
else { | |
//we are null. tell our parent if they don't know. | |
if (newValue !== this.props.value) { | |
this.props.onChange(newValue); | |
} | |
} | |
} | |
discardValue() { | |
this.setState({ value: this.formatValue(this.props.value) }); | |
} | |
handleBlur(event) { | |
const parsedDate = this.parseDate(event.target.value); | |
this.fireChangeEvent(parsedDate); | |
//when we leave, change the displayed value back to the external one | |
// this.setState({ value: this.props.value }); | |
if (this.props.onBlur) { | |
return this.props.onBlur(event); | |
} | |
} | |
handleChange(event) { | |
this.setState({ value: event.target.value }); | |
} | |
componentWillReceiveProps(nextProps) { | |
// if we get a NEW value from our parent, accept it. | |
if (nextProps.value !== this.props.value || document.activeElement !== this.refs.input) { | |
this.setState({ value: this.formatValue(nextProps.value) }); | |
} | |
} | |
// takes a string or null (if a string, it is expected to be a valid date | |
// in the strict DEFAULT_DATA_FORMAT) | |
// outputs a string in the display format. | |
formatValue(value) { | |
if (!value) return ''; | |
return moment(value, this.dataFormat).format(this.displayFormat); | |
} | |
//takes a string and returns null (e.g. for empty string) or a moment (that may be invalid!) | |
parseDate(input) { | |
if (!input) return null; | |
input = input.trim(); | |
if (input.length === 0) return null; | |
return moment(input, this.acceptedFormats); | |
} | |
focus() { | |
this.refs.input.focus(); | |
} | |
render() { | |
// eslint-disable-next-line no-unused-vars | |
const { onChange, onBlur, value, acceptedFormats, displayFormat, dataFormat, ...props } = this.props; | |
return <input | |
type="text" | |
{...props} | |
ref='input' | |
onBlur={this.handleBlur} | |
onChange={this.handleChange} | |
value={this.state.value} | |
/>; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment