thinkful-ui
Version:
Shared navigation and UI resources for Thinkful.
170 lines (142 loc) • 5.17 kB
JSX
const React = require('react');
const moment = require('moment-timezone');
const cx = require('classnames');
const _ = require('lodash');
require('./datepicker.less');
/**
* Day component
8 @property date {DateTime} of the date to be displayed
*/
class Day extends React.Component {
static propTypes = {
day: React.PropTypes.string
}
render () {
const {date, unclickable, otherMonth, active, onClick} = this.props;
const classes = cx(
"day",
{unclickable: unclickable},
{'other-month': otherMonth},
{active: active},
{today: moment().dayOfYear() === moment(date).dayOfYear()});
const isToday = moment(date).dayOfYear() === moment().dayOfYear();
const isFirstOfMonth = moment(date).date() === 1;
const monthName = moment(date).format('MMM')
return (
<div
className={classes}
onClick={onClick.bind()}>
{isFirstOfMonth && <div className="day-tiny-text">{monthName}</div>}
{isToday && <div className="day-tiny-text">Today</div>}
{moment(date).date()}
</div>
);
}
}
class DatePicker extends React.Component {
static displayName = "DatePicker"
constructor() {
super();
this.state = {
visible: false,
days: [],
activeIndex: null,
monthsNavigated: 0
}
}
componentDidMount() {
this._generateDays(this.props.defaultDate);
}
componentWillReceiveProps(newProps) {
if (this.props.defaultDate.dayOfYear() !== newProps.defaultDate.dayOfYear()) {
this._generateDays(newProps.defaultDate);
}
}
_generateDays(defaultDate=false) {
let {monthsNavigated, activeIndex} = this.state;
// If called on initial render, check defaultDate to determine if calendar
// should start on a month different than the current one
if (defaultDate) {
monthsNavigated = defaultDate.month() - moment().month();
}
const startDay = moment().add(monthsNavigated, 'month').
startOf('month').startOf('week').startOf('day');
const endDay = moment().add(monthsNavigated, 'month').
endOf('month').endOf('week').startOf('day');
const totalDays = endDay.diff(startDay, 'days') + 1;
const days = _.map(Array(totalDays), (i, idx) => {
return {
dateObj: moment(startDay).add(idx, 'day'),
dayOfYear: moment(startDay).add(idx, 'day').dayOfYear()
}});
// Keep existing activeIndex if it is defined and a new defaultDate has not come thru
activeIndex = (!! defaultDate || ! activeIndex || activeIndex === -1) ?
_.findIndex(days, {dayOfYear: moment(defaultDate || '').dayOfYear()}) : activeIndex;
this.setState({
days: days,
activeIndex: activeIndex,
monthsNavigated: monthsNavigated
});
}
_handleClick(event, newDay) {
const {days} = this.state;
const {handleChange} = this.props;
const newActiveIndex = _.findIndex(days, {dayOfYear: newDay});
this.setState({activeIndex: newActiveIndex});
this._toggleOpen();
handleChange(days[newActiveIndex].dateObj);
}
_navigateForward() {
this.state.monthsNavigated = this.state.monthsNavigated + 1;
this._generateDays();
}
_navigateBack() {
this.state.monthsNavigated = this.state.monthsNavigated - 1;
this._generateDays();
}
_toggleOpen() {
this.setState({visible: !this.state.visible});
}
render() {
const {className} = this.props;
const {days, activeIndex, monthsNavigated, visible} = this.state;
const activeDay = days[activeIndex] && days[activeIndex].dateObj;
const datePickerClasses = cx('date-picker', {hidden: !visible});
return (
<div className={cx("date-picker-container", className)}>
<div
className="button date-picker-button"
onClick={this._toggleOpen.bind(this)}>
{moment(activeDay).format('MM/DD/YYYY')}
<span className="icon-navigatedown" aria-hidden="true"></span>
</div>
<div className={datePickerClasses}>
<span
className="icon-navigateleft" aria-hidden="true"
onClick={this._navigateBack.bind(this)}></span>
<span
className="icon-navigateright" aria-hidden="true"
onClick={this._navigateForward.bind(this)}></span>
<div className="selected-day">
{moment(activeDay).format('dddd, MMMM Do')}
</div>
<div className="day-headings">
{['S', 'M', 'T', 'W', 'H', 'F', 'S'].
map((day, key) => <div key={key} className="day-heading">{day}</div>)}
</div>
<div className="days-container">
{days.map((day, key) => <Day
date={day.dateObj}
key={key}
active={key===activeIndex}
otherMonth={day.dateObj.month() !==
moment().add(monthsNavigated, 'month').month()}
unclickable={false}
onClick={event => this._handleClick(event, day.dayOfYear)}/>)}
</div>
</div>
</div>
);
}
}
export {DatePicker}