var CalendarViewType = { CALENDAR: 1, MONTH: 2, YEAR: 3 }; var DAYS_IN_WEEK = 7; var MONTHS_IN_YEAR = 12; var MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; var MONTHS_ABBREVIATED = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; var DAYS_OF_WEEK = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; var getFirstOfMonthDate = function(dt) { var dtStart = new Date(dt.getTime()); dtStart.setDate(1); return dtStart; }; var getLastOfMonthDate = function(dt) { var dtEnd = new Date(dt.getTime()); dtEnd.setDate(1); dtEnd.setMonth(dtEnd.getMonth() + 1); dtEnd.setDate(dtEnd.getDate() - 1); return dtEnd; }; var getEndOfLastMonthDate = function(dt) { var dtLast = getFirstOfMonthDate(dt); dtLast.setDate(dtLast.getDate() - 1); return dtLast; } var generateCalendar = function(dt) { var dtStart = getFirstOfMonthDate(dt); var dtEnd = getLastOfMonthDate(dt); var dtEndLastMonth = getEndOfLastMonthDate(dt); var calendar = [ _.range( dtEndLastMonth.getDate() + 1 - dtStart.getDay(), dtEndLastMonth.getDate() + 1 ).concat( _.range(1, DAYS_IN_WEEK - dtStart.getDay() + 1) ) ]; var dayCount = calendar[0][DAYS_IN_WEEK - 1] + 1; while (dayCount <= dtEnd.getDate() - DAYS_IN_WEEK) { var endDay = dayCount + DAYS_IN_WEEK; calendar.push(_.range(dayCount, endDay)); dayCount = endDay; } calendar.push( _.range( dayCount, dtEnd.getDate() + 1 ).concat( _.range(1, DAYS_IN_WEEK - dtEnd.getDay()) ) ); return calendar; }; var CalendarHeader = React.createClass({ render: function() { return (
{this.props.title}
); }, handleClickPrev: function() { if (this.props.onClickPrev) { this.props.onClickPrev(); } }, handleClickNext: function() { if (this.props.onClickNext) { this.props.onClickNext(); } }, handleClickHeader: function() { if (this.props.onClickHeader) { this.props.onClickHeader(); } } }); var YearGrid = React.createClass({ COLS_PER_ROW: 5, render: function() { var yearGrid = _.range( this.props.startYear, this.props.startYear + this.props.range, this.COLS_PER_ROW ).map(function(startYear) { return {this.renderRow(startYear, startYear + this.COLS_PER_ROW)}; }.bind(this)); }, renderRow: function(startYear, endYear) { return _.range(startYear, endYear).map(function(year) { return ( {year} ) }.bind(this)); }, handleClickYearWrapper: function(year) { return function() { this.handleClickYear(year) }.bind(this); }, handleClickYear: function(year) { if (this.props.onClickYear) { this.props.onClickYear(year); } } }); var YearView = React.createClass({ YEAR_RANGE: 20, getInitialState: function() { return { selectedDate: this.props.selectedDate, viewingMonth: this.props.viewingMonth, startYear: this.props.viewingYear - (this.props.viewingYear % this.YEAR_RANGE) }; }, render: function() { return (
); }, handleClickPrev: function() { this.setState({ viewingYear: this.state.viewingYear - this.YEAR_RANGE }); }, handleClickNext: function() { this.setState({ viewingYear: this.state.viewingYear + this.YEAR_RANGE }); }, handleClickYear: function(year) { if (this.props.onClickYear) { this.props.onClickYear(month, year); } } }); var MonthGrid = React.createClass({ COLS_PER_ROW: 3, render: function() { var monthGrid = _.range(0, MONTHS.length, this.COLS_PER_ROW).map(function(startCol) { return {this.renderRow(startCol, startCol + this.COLS_PER_ROW)}; }.bind(this)); return {monthGrid}
; }, renderRow: function(startCol, endCol) { return _.range(startCol, endCol).map(function(monthIndex) { return ( {MONTHS_ABBREVIATED[monthIndex]} ) }.bind(this)); }, handleClickMonthWrapper: function(monthIndex) { return function() { this.handleClickMonth(monthIndex) }.bind(this); }, handleClickMonth: function(monthIndex) { if (this.props.onClickMonth) { this.props.onClickMonth(monthIndex); } } }); var MonthView = React.createClass({ getInitialState: function() { return { selectedDate: this.props.selectedDate, viewingMonth: this.props.viewingMonth, viewingYear: this.props.viewingYear }; }, render: function() { return (
); }, handleClickPrev: function() { this.setState({viewingYear: this.state.viewingYear - 1}); }, handleClickNext: function() { this.setState({viewingYear: this.state.viewingYear + 1}); }, handleClickHeader: function() { if (this.props.onClickHeader) { this.props.onClickHeader(this.state.viewingMonth, this.state.viewingYear); } }, handleClickMonth: function(month) { if (this.props.onClickMonth) { this.props.onClickMonth(month, this.state.viewingYear); } } }); var DayOfWeekRow = React.createClass({ render: function() { var daysRow = DAYS_OF_WEEK.map(function(day) { return {day}; }); return {daysRow}; } }); var CalendarGrid = React.createClass({ render: function() { var dt = new Date(this.props.year, this.props.month, 1); var calendar = generateCalendar(dt); var calendarRows = calendar.map(function(row, index) { return {this.renderRow(row, index)}; }.bind(this)); return {calendarRows}
; }, renderRow: function(row, rowIndex) { return row.map(function(day) { return {day}; }); } }); var CalendarView = React.createClass({ getInitialState: function() { return { selectedDate: this.props.selectedDate, viewingMonth: this.props.viewingMonth, viewingYear: this.props.viewingYear }; }, render: function() { return (
); }, handleClickPrev: function() { var updatedMonth = this.state.viewingMonth - 1; var updatedYear = this.state.viewingYear; if (updatedMonth === -1) { updatedMonth = MONTHS_IN_YEAR - 1; updatedYear -= 1; } this.setState({viewingMonth: updatedMonth, viewingYear: updatedYear}); }, handleClickNext: function() { var updatedMonth = this.state.viewingMonth + 1; var updatedYear = this.state.viewingYear; if (updatedMonth === MONTHS_IN_YEAR) { updatedMonth %= MONTHS_IN_YEAR; updatedYear += 1; } this.setState({viewingMonth: updatedMonth, viewingYear: updatedYear}); }, handleClickHeader: function() { if (this.props.onClickHeader) { this.props.onClickHeader(this.state.viewingMonth, this.state.viewingYear); } } }); var CalendarWidget = React.createClass({ getInitialState: function() { var today = new Date(); return { selectedDate: today, viewingMonth: today.getMonth(), viewingYear: today.getFullYear(), view: CalendarViewType.CALENDAR }; }, render: function() { var calendarView = null; if (this.state.view === CalendarViewType.CALENDAR) { calendarView = ( ); } else if (this.state.view === CalendarViewType.MONTH) { calendarView = ( ); } else if (this.state.view === CalendarViewType.YEAR) { calendarView = ( ); } return
{calendarView}
}, updateDate: function(day, month, year) { }, showCalendarView: function(month, year) { this.setState({view: CalendarViewType.CALENDAR, viewingMonth: month, viewingYear: year}); }, showMonthView: function(month, year) { this.setState({view: CalendarViewType.MONTH, viewingMonth: month, viewingYear: year}); }, showYearView: function(month, year) { this.setState({view: CalendarViewType.YEAR, viewingMonth: month, viewingYear: year}); } }); React.render( , document.getElementById('calendar') );