UNPKG

react-conventions

Version:

An open source set of React components that implement Ambassador's Design and UX patterns.

361 lines (316 loc) 9.9 kB
import React from 'react' import moment from 'moment' import SelectField from '../SelectField/SelectField' import style from './style.scss' import optclass from '../internal/OptClass' import DateHelper from './DateHelper' /** * The DatePicker component. */ class DatePicker extends React.Component { constructor(props) { super(props) } _dateHelper = this.props.dateHelper ? this.props.dateHelper : DateHelper state = { year: { min: 0, value: 0, max: 0, options: [] }, month: { min: 0, value: 0, max: 0, options: [] }, day: { min: 0, value: 0, max: 0, options: [] }, value: '' } static defaultProps = { min: { month: '-0', day: '-0', year: '-10' }, max: { month: '+0', day: '+0', year: '+10' }, format: 'YYYY-MM-DD' } static propTypes = { /** * Max date - object with month, day, year. */ max: React.PropTypes.object, /** * Min date - object with month, day, year. */ min: React.PropTypes.object, /** * Date string. */ value: React.PropTypes.string, /** * Date format - any valid Moment.js format string. */ format: React.PropTypes.string, /** * A callback function to be called when the value changes. */ changeCallback: React.PropTypes.func, /** * Optional CSS class(es) to be used for local styles (string or array of strings) */ optClass: React.PropTypes.oneOfType([ React.PropTypes.array, React.PropTypes.string ]), /** * If true, will display the inputs inline on smaller screens (default 100% width) */ inlineSmallScreen: React.PropTypes.bool } _initDate = (date, format) => { let dateObj = { year: { min: 0, value: 0, max: 0, options: [] }, month: { min: 0, value: 0, max: 0, options: [] }, day: { min: 0, value: 0, max: 0, options: [] }, value: '' } let mDate = date === undefined ? moment.utc() : moment.utc(date, format) // selected date values dateObj.year.value = this._dateHelper.getYear(mDate) dateObj.month.value = this._dateHelper.getMonth(mDate) dateObj.day.value = this._dateHelper.getDate(mDate) dateObj.value = mDate.format(format) // min & max values dateObj.year.min = this._getMinOrMax(this.props.min, 'year') dateObj.year.max = this._getMinOrMax(this.props.max, 'year') dateObj.month.min = this._getMinOrMax(this.props.min, 'month') dateObj.month.max = this._getMinOrMax(this.props.max, 'month') dateObj.day.min = this._getMinOrMax(this.props.min, 'day') dateObj.day.max = this._getMinOrMax(this.props.max, 'day') // options dateObj.year.options = this._getYears(dateObj.year.min, dateObj.year.max) dateObj.month.options = this._getMonths(dateObj) dateObj.day.options = this._getDays(dateObj) this.setState(dateObj) } /** * * @param {Object} minOrMax Example: { month: '-0', day: '-0', year: '-10' } | { month: 'current', day: 'current', year: 'current' } * @param {String} type String. Options: ['year', 'month', 'day'] * @returns {Number} Calculated min or max value for given type * @private */ _getMinOrMax = (minOrMax, type) => { let momentDate let value if (minOrMax[type] === 'current') { momentDate = moment.utc() } else if (minOrMax[type].indexOf('+') !== -1) { momentDate = moment.utc().add(Math.abs(minOrMax[type]), type) } else if (minOrMax[type].indexOf('-') !== -1) { momentDate = moment.utc().subtract(Math.abs(minOrMax[type]), type) } else { value = minOrMax[type] } if (momentDate) { switch (type) { case 'year': value = this._dateHelper.getYear(momentDate) break case 'month': value = this._dateHelper.getMonth(momentDate) break case 'day': value = this._dateHelper.getDate(momentDate) break } } return parseInt(value) } /** * * @param {Integer} min First year * @param {Integer} max Last year * @returns {Array} Array of objects, ex. [{ value: '2010' }, (...), { value: '2020' }] * @private */ _getYears = (min, max) => { let yearOptions = [] for (var i=min; i<=max; i++) { yearOptions.push({value: i.toString()}) } return yearOptions } /** * * @param {Object} dateObj State object * @returns {Array} Array of objects, ex. [{ value: '0', display: 'Jan' }, (...), { value: '11', display: 'Dec' }] * @private */ _getMonths = (dateObj) => { let monthOptions = [] const checkMin = dateObj.year.value === dateObj.year.min const checkMax = dateObj.year.value === dateObj.year.max let start = checkMin ? dateObj.month.min : 0 let end = checkMax ? dateObj.month.max+1 : 12 for (var i=start; i<end; i++) { monthOptions.push({value: i.toString(), display: moment.utc(i+1, 'MM').format('MMM')}) } // if selected month is greater than max month, change it to max month if (checkMax && dateObj.month.value > dateObj.month.max) { dateObj.month.value = dateObj.month.max } // if selected month is lower than min month, change it to min month if (checkMin && dateObj.month.value < dateObj.month.min) { dateObj.month.value = dateObj.month.min } return monthOptions } /** * * @param {Object} dateObj State object * @returns {Array} Array of objects, ex. [{ value: '1' }, (...), { value: '31' }] * @private */ _getDays = (dateObj) => { let dayOptions = [] const checkMin = dateObj.year.value === dateObj.year.min && dateObj.month.value === dateObj.month.min const checkMax = dateObj.year.value === dateObj.year.max && dateObj.month.value === dateObj.month.max const daysInMonth = moment.utc(dateObj.year.value+'-'+(dateObj.month.value+1), 'YYYY-M').daysInMonth() let start = checkMin ? dateObj.day.min : 1 let end = checkMax ? dateObj.day.max : daysInMonth for (var i=start; i<=end; i++) { dayOptions.push({value: i.toString()}) } // if selected day is greater than max day in a month, change it to max day in a month if (dateObj.day.value > daysInMonth) { dateObj.day.value = daysInMonth } // if selected day is greater than max day, change it to max day if (checkMax && dateObj.day.value > dateObj.day.max) { dateObj.day.value = dateObj.day.max } // if selected day is lower than min day, change it to min day if (checkMin && dateObj.day.value < dateObj.day.min) { dateObj.day.value = dateObj.day.min } dateObj.value = this._getValue(dateObj, this.props.format) return dayOptions } /** * * @param {Object} state State object * @param {String} format String with a valid moment.js format, ex. 'YYYY-MM-DD' * @returns {String} Date string according to passed format, ex. '2016-09-04' * @private */ _getValue = (state, format) => { return moment.utc().year(state.year.value).month(state.month.value).date(state.day.value).format(format) } handleChangeYear = (event) => { let state = this.state state.year.value = parseInt(event.target.value) state.value = this._getValue(state, this.props.format) state.month.options = this._getMonths(state) state.day.options = this._getDays(state) this.setState({ year: state.year, month: state.month, day: state.day, value: state.value }, () => { this.callback(state.value) }) } handleChangeMonth = (event) => { let state = this.state state.month.value = parseInt(event.target.value) state.value = this._getValue(state, this.props.format) state.day.options = this._getDays(state) this.setState({ month: state.month, day: state.day, value: state.value }, () => { this.callback(state.value) }) } handleChangeDay = (event) => { let state = this.state state.day.value = parseInt(event.target.value) state.value = this._getValue(state, this.props.format) this.setState({ day: state.day, value: state.value }, () => { this.callback(state.value) }) } callback = (value) => { if (typeof this.props.changeCallback === 'function') { this.props.changeCallback({ target: { name: this.props.name, value: value } }) } } componentWillMount = () => { this._initDate(this.props.value, this.props.format) } componentWillReceiveProps = (nextProps) => { if (nextProps.value !== this.props.value) { this._initDate(nextProps.value, this.props.format) } } render() { const inlineSmallScreen = this.props.inlineSmallScreen ? style['inline-small-screen'] : null const componentClass = optclass(style, ['datepicker-component', inlineSmallScreen], this.props.optClass) return ( <div className={componentClass}> <SelectField options={this.state.month.options} valueProp='value' displayProp='display' value={this.state.month.value.toString()} changeCallback={this.handleChangeMonth} /> <SelectField options={this.state.day.options} valueProp='value' displayProp='value' value={this.state.day.value.toString()} changeCallback={this.handleChangeDay} /> <SelectField options={this.state.year.options} valueProp='value' displayProp='value' value={this.state.year.value.toString()} changeCallback={this.handleChangeYear} /> </div> ) } } export default DatePicker