|
import { |
|
addDays, |
|
addMonths, |
|
getDaysInMonth, |
|
isAfter, |
|
isBefore, |
|
isEqual, |
|
lastDayOfMonth, |
|
setDate, |
|
subDays, |
|
subMonths, |
|
} from 'date-fns'; |
|
import React, {Component} from 'react'; |
|
import DatePickerCalendar from './DatePickerCalendar'; |
|
import DatePickerNav from './DatePickerNav'; |
|
import PropTypes from 'prop-types'; |
|
|
|
export default class DatePickerDialog extends Component { |
|
static propTypes = { |
|
endDate: PropTypes.instanceOf(Date), |
|
isRange: PropTypes.bool, |
|
maxDate: PropTypes.instanceOf(Date), |
|
minDate: PropTypes.instanceOf(Date), |
|
onUpdate: PropTypes.func, |
|
resolve: PropTypes.func, |
|
startDate: PropTypes.instanceOf(Date), |
|
}; |
|
|
|
constructor(props) { |
|
super(props); |
|
this.state = { |
|
startDate: props.startDate, |
|
endDate: props.endDate, |
|
visibleDate: props.startDate, |
|
}; |
|
} |
|
|
|
onNext = () => |
|
this.setState(prevState => { |
|
const nextMonth = addMonths(prevState.visibleDate, 1); |
|
const day = Math.min( |
|
getDaysInMonth(nextMonth), |
|
prevState.visibleDate.getDate() |
|
); |
|
if ( |
|
!this.props.maxDate || |
|
isBefore(nextMonth, this.props.maxDate) |
|
) { |
|
const visibleDate = setDate(nextMonth, day); |
|
return { |
|
visibleDate, |
|
}; |
|
} |
|
return { |
|
visibleDate: this.props.maxDate, |
|
}; |
|
}); |
|
|
|
onPrev = () => |
|
this.setState(prevState => { |
|
const prevMonth = lastDayOfMonth( |
|
subMonths( |
|
new Date( |
|
prevState.visibleDate.getFullYear(), |
|
prevState.visibleDate.getMonth() |
|
), |
|
1 |
|
) |
|
); |
|
const day = Math.min( |
|
getDaysInMonth(prevMonth), |
|
prevState.visibleDate.getDate() |
|
); |
|
if (!this.props.minDate || isAfter(prevMonth, this.props.minDate)) { |
|
const visibleDate = setDate(prevMonth, day); |
|
return { |
|
visibleDate, |
|
}; |
|
} |
|
return { |
|
visibleDate: this.props.minDate, |
|
}; |
|
}); |
|
|
|
onClickJump = date => { |
|
if (!this.props.isRange) { |
|
this.props.resolve({startDate: date}); |
|
} else { |
|
this.setStartAndEnd(date); |
|
} |
|
}; |
|
|
|
onClickDate = date => { |
|
if (!this.props.isRange) { |
|
this.props.resolve({startDate: date}); |
|
} else { |
|
this.setStartAndEnd(date); |
|
} |
|
}; |
|
|
|
onClickNav = date => { |
|
this.setState({visibleDate: date}); |
|
}; |
|
|
|
setStartAndEnd = date => { |
|
const {startDate, endDate} = this.state; |
|
const newDates = { |
|
startDate, |
|
endDate, |
|
}; |
|
if (isEqual(date, startDate)) { |
|
// reset start and end dates anchored to start |
|
newDates.startDate = date; |
|
newDates.endDate = addDays(date, 1); |
|
} else if (isEqual(date, endDate)) { |
|
// reset start and end dates anchored to end |
|
newDates.startDate = subDays(date, 1); |
|
newDates.endDate = date; |
|
} else if (isBefore(date, startDate)) { |
|
// extend the start date |
|
newDates.startDate = date; |
|
} else if (isAfter(date, endDate)) { |
|
// extend the end date |
|
newDates.endDate = date; |
|
} else if (isBefore(date, endDate)) { |
|
// truncate end date |
|
newDates.endDate = date; |
|
} |
|
this.setState({...newDates, visibleDate: date}); |
|
this.props.onUpdate(newDates); |
|
}; |
|
|
|
render() { |
|
const {isRange} = this.props; |
|
const {startDate, endDate, visibleDate} = this.state; |
|
return ( |
|
<div className="calendar"> |
|
<DatePickerNav |
|
visibleDate={visibleDate} |
|
startDate={startDate} |
|
endDate={endDate} |
|
isRange={isRange} |
|
onNext={this.onNext} |
|
onPrev={this.onPrev} |
|
onJump={this.onClickNav} |
|
/> |
|
<DatePickerCalendar |
|
visibleDate={visibleDate} |
|
startDate={startDate} |
|
endDate={endDate} |
|
isRange={isRange} |
|
minDate={this.props.minDate} |
|
maxDate={this.props.maxDate} |
|
onClickDate={this.onClickDate} |
|
onClickJump={this.onClickJump} |
|
/> |
|
</div> |
|
); |
|
} |
|
} |
Great work. Have you considered publishing it to npm?