atsid/react-calendar-render

View on GitHub
src/CalendarMonthView.js

Summary

Maintainability
A
2 hrs
Test Coverage
const React = require('react');
const { findDOMNode } = require('react-dom');
const classNames = require('classnames');
const DefaultDayRenderer = require('./CalendarDayRenderer');

class CalendarMonthView extends React.Component {
  componentDidMount() {
    window.addEventListener('resize', this.handleResize.bind(this));
    this.handleResize();
  }

  componentDidUpdate() {
    this.handleResize();
  }

  /**
   * Gets the number of weeks in the current month.  Formula is taken from answer on
   * http://stackoverflow.com/questions/2483719/get-weeks-in-month-through-javascript
   *
   * @returns {number} number of weeks in the current month
   */
  getRowCount() {
    let weekCount = this.props.forceSixWeek ? 6 : 0;

    if (!weekCount) {
      const year = this.props.month.year();
      const month = this.props.month.month();
      const firstOfMonth = new Date(year, month, 1);
      const lastOfMonth = new Date(year, month + 1, 0);
      const used = firstOfMonth.getDay() + lastOfMonth.getDate();

      weekCount = Math.ceil(used / 7);
    }

    return weekCount;
  }

  getPercent(index, rowCount) {
    const percent = (index / rowCount) * 100;

    return `${percent}%`;
  }

  getHeaderNode(rootNode) {
    let headerNode;

    if (this.props.showWeekHeader) {
      headerNode = rootNode.childNodes[0];
    }

    return headerNode;
  }

  getRowNodes(rootNode) {
    let rowNodes;

    if (this.props.showWeekHeader) {
      rowNodes = rootNode.childNodes[1].childNodes;
    } else {
      rowNodes = rootNode.childNodes[0].childNodes;
    }

    return rowNodes;
  }

  getRowTop(headerNode) {
    let top = 0;

    if (this.props.showWeekHeader && headerNode) {
      top = headerNode.clientHeight;
    }

    return top;
  }

  getWeekRow() {
    const weekdayRow = classNames('calendar-week-row');
    const weekdayClass = classNames('calendar-weekday');
    let row = null;

    if (this.props.showWeekHeader) {
      row = (
        <div className={weekdayRow}>
          <table>
            <tbody>
            <tr>
              <td className={weekdayClass}>{this.props.weekdays[0]}</td>
              <td className={weekdayClass}>{this.props.weekdays[1]}</td>
              <td className={weekdayClass}>{this.props.weekdays[2]}</td>
              <td className={weekdayClass}>{this.props.weekdays[3]}</td>
              <td className={weekdayClass}>{this.props.weekdays[4]}</td>
              <td className={weekdayClass}>{this.props.weekdays[5]}</td>
              <td className={weekdayClass}>{this.props.weekdays[6]}</td>
            </tr>
            </tbody>
          </table>
        </div>
      );
    }

    return row;
  }

  handleResize() {
    const rootNode = findDOMNode(this);
    const headerNode = this.getHeaderNode(rootNode);
    const rowNodes = this.getRowNodes(rootNode);
    const height = headerNode ? rootNode.clientHeight - headerNode.clientHeight : rootNode.clientHeight;
    const rowHeight = Math.floor(height / rowNodes.length);
    const top = this.getRowTop(headerNode);

    for (let index = 0; index < rowNodes.length; index++) {
      const rowTop = top + (rowHeight - 1) * index;

      if (index !== rowNodes.length - 1) {
        rowNodes[index].style.top = `${rowTop}px`;
        this.resizeRow(rowNodes[index], rowHeight);
      } else {
        this.resizeRow(rowNodes[index], height);
        rowNodes[index].style.top = `${rowTop}px`;
        rowNodes[index].style.bottom = '0px';
      }
    }
  }

  resizeRow(rowNode, height) {
    rowNode.style.height = `${height}px`;
    rowNode.childNodes[0].style.height = `${height}px`;
    rowNode.childNodes[1].style.height = `${height}px`;
  }

  renderDay(day) {
    const DayRenderer = this.props.dayRenderer;
    const renderedDay = (<DayRenderer month={this.props.month} day={day.clone()}/>);

    day.add(1, 'day');

    return renderedDay;
  }

  render() {
    const rowCount = this.getRowCount();
    const tableClassName = classNames('calendar-mv-position');
    const tableClassName2 = classNames('calendar-mv-bg-position');
    const tableCellClassName = classNames('calendar-mv-cell');
    const tableCellBackgroundClassName = classNames('calendar-mv-bg-cell');
    const calendarClassName = classNames('calendar-mv-container');
    const calendarContainerName = classNames('calendar-mv-row-container');
    const month2 = this.props.month.clone().startOf('month').day('Sunday');
    const tableRow = classNames('calendar-mv-row');
    const weekRow = this.getWeekRow();

    return (
      <div className={calendarClassName}>
        {weekRow}
        <div className={calendarContainerName}>
          {Array(rowCount).fill().map((data, index) => {
            return (
              <div className={tableRow}
                   key={index}
                   style={{top: this.getPercent(index, rowCount)}}>
                <table className={tableClassName2}>
                  <tbody>
                  <tr>
                    <td className={tableCellBackgroundClassName}/>
                    <td className={tableCellBackgroundClassName}/>
                    <td className={tableCellBackgroundClassName}/>
                    <td className={tableCellBackgroundClassName}/>
                    <td className={tableCellBackgroundClassName}/>
                    <td className={tableCellBackgroundClassName}/>
                    <td className={tableCellBackgroundClassName}/>
                  </tr>
                  </tbody>
                </table>
                <table className={tableClassName}>
                  <tbody>
                  <tr>
                    <td className={tableCellClassName}>{this.renderDay(month2)}</td>
                    <td className={tableCellClassName}>{this.renderDay(month2)}</td>
                    <td className={tableCellClassName}>{this.renderDay(month2)}</td>
                    <td className={tableCellClassName}>{this.renderDay(month2)}</td>
                    <td className={tableCellClassName}>{this.renderDay(month2)}</td>
                    <td className={tableCellClassName}>{this.renderDay(month2)}</td>
                    <td className={tableCellClassName}>{this.renderDay(month2)}</td>
                  </tr>
                  </tbody>
                </table>
              </div>
            );
          })}
        </div>
      </div>
    );
  }
}

CalendarMonthView.propTypes = {
  forceSixWeek: React.PropTypes.bool,
  month: React.PropTypes.object.isRequired,
  showWeekHeader: React.PropTypes.bool,
  dayRenderer: React.PropTypes.func,
  weekdays: React.PropTypes.array,
};

CalendarMonthView.defaultProps = {
  forceSixWeek: false,
  showWeekHeader: true,
  dayRenderer: DefaultDayRenderer,
  weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
};

module.exports = CalendarMonthView;