Created
September 29, 2016 05:00
-
-
Save zeroasterisk/40830883564c2822bff98cd616964841 to your computer and use it in GitHub Desktop.
react-dates & uniforms = start/stop date range picker glory
This file contains 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 from 'react'; | |
import { DateRangePicker } from 'react-dates'; | |
import momentPropTypes from 'react-moment-proptypes'; | |
class DateRangePickerWrapper extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
focusedInput: null, | |
startDate: props.startDate || null, | |
endDate: props.startDate || null, | |
}; | |
this.onDatesChange = this.onDatesChange.bind(this); | |
this.onFocusChange = this.onFocusChange.bind(this); | |
} | |
componentWillReceiveProps(nextProps) { | |
const { startDate, endDate } = nextProps; | |
if (startDate && endDate) { | |
this.setState({ startDate, endDate }); | |
} | |
} | |
onDatesChange({ startDate, endDate }) { | |
this.props.handleDatesChange({ startDate, endDate }); | |
this.setState({ startDate, endDate }); | |
} | |
onFocusChange(focusedInput) { | |
this.setState({ focusedInput }); | |
} | |
render() { | |
const { focusedInput, startDate, endDate } = this.state; | |
return ( | |
<div> | |
<DateRangePicker | |
{...this.props} | |
onDatesChange={this.onDatesChange} | |
onFocusChange={this.onFocusChange} | |
focusedInput={focusedInput} | |
startDate={startDate} | |
endDate={endDate} | |
/> | |
</div> | |
); | |
} | |
} | |
const datePropTypes = React.PropTypes.oneOfType([ | |
React.PropTypes.string, | |
React.PropTypes.number, | |
React.PropTypes.instanceOf(Date), | |
momentPropTypes.momentObj, | |
]); | |
DateRangePickerWrapper.propTypes = { | |
startDate: datePropTypes, | |
endDate: datePropTypes, | |
handleDatesChange: React.PropTypes.func.isRequired, | |
}; | |
export default DateRangePickerWrapper; |
This file contains 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
/** | |
* Date Picker for Start, Stop dates | |
* | |
* schema fields (default): start, date, stats.durationDays | |
* | |
* Uses: | |
* https://github.com/vazco/uniforms | |
* https://github.com/airbnb/react-dates | |
* | |
* NOTE: | |
* For simple "external" state I created a non-uniforms wrapper | |
* ./DateRangePickerWrapper.js | |
* | |
* This allows the DateRangePickerWrapper to keep it's moment objects in | |
* DateRangePickerWrapper state as well as it's inputFocus. | |
* | |
* DateRangePickerWrapper will trigger handleDatesChange from this component. | |
* | |
* NOTE: | |
* I have a durationDays field, but if you don't have or want it, remove all | |
* instances an it would disappear. If you do not remove it, uniforms will | |
* complain that the field isn't found in the schema. | |
*/ | |
import _ from 'lodash'; | |
import React from 'react'; | |
import classnames from 'classnames'; | |
import moment from 'moment-timezone'; | |
import { BaseField } from 'uniforms'; | |
import DateRangePickerWrapper from './DateRangePickerWrapper'; | |
// Date cleanup and handlers from uniforms DateInput | |
const makeDate = str => { | |
if (!str) return null; | |
if (_.isDate(str) || _.isString(str) || _.isNumber(str)) { | |
return moment(str).tz(moment.tz.guess()).toDate(); | |
} | |
// console.error('makeDate invalid input', str); | |
return null; | |
}; | |
// Here's the actual Uniforms enabled field | |
// NOTE this is using direct context vs. connectField() | |
// could possibly be refactored... | |
const DateStartStopInput = (props, { uniforms: { model, onChange } }) => { | |
const { | |
required, | |
startField, | |
endField, | |
durationDaysField, | |
tzField, | |
} = props; | |
const handleDatesChange = ({ startDate, endDate }) => { | |
// console.log('handleDatesChange', startDate, endDate); | |
if (!(startDate && endDate)) return null; | |
// calculate duration | |
const durationDays = moment(endDate).diff(startDate, 'days'); | |
// save into Uniforms | |
onChange(startField, startDate.toDate()); | |
onChange(endField, endDate.toDate()); | |
onChange(durationDaysField, durationDays); | |
return null; | |
}; | |
// get dates from model (from uniforms) as momentObjects | |
const startDate = moment(_.result(model, startField)); | |
const endDate = moment(_.result(model, endField)); | |
const durationDays = Math.max(1, _.result(model, durationDaysField)); | |
// calculate timezone | |
const tz = moment(startDate).tz(moment.tz.guess()).format('z'); | |
return ( | |
<div className={classnames( | |
'form-group row', | |
{ required } | |
)}> | |
<label className="control-label col-sm-3"> | |
Flight Dates | |
</label> | |
<div className="col-sm-9"> | |
<div className="pull-sm-left m-r-1"> | |
<DateRangePickerWrapper | |
{...props} | |
handleDatesChange={handleDatesChange} | |
startDate={startDate} | |
endDate={endDate} | |
/> | |
</div> | |
<div className="pull-sm-left small"> | |
<div title="timezone" className="text-muted"> | |
<small> | |
{tz} | |
</small> | |
</div> | |
<div title="duration" className="text-muted"> | |
<small> | |
{durationDays} days | |
</small> | |
</div> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
DateStartStopInput.propTypes = { | |
orderLine: React.PropTypes.object, | |
startField: React.PropTypes.string, | |
endField: React.PropTypes.string, | |
durationDaysField: React.PropTypes.string, | |
tzField: React.PropTypes.string, | |
required: React.PropTypes.bool, | |
start: React.PropTypes.instanceOf(Date), | |
}; | |
DateStartStopInput.defaultProps = { | |
startField: 'start', | |
endField: 'stop', | |
durationDaysField: 'stats.durationDays', | |
tzField: 'tz', | |
}; | |
DateStartStopInput.contextTypes = BaseField.contextTypes; | |
export default DateStartStopInput; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment