forked from mirrors/gecko-dev
		
	# ignore-this-changeset Differential Revision: https://phabricator.services.mozilla.com/D36053 --HG-- extra : source : 651d8f947a29f5d80b7e833f7e6b99e2afe8bf9d
		
			
				
	
	
		
			170 lines
		
	
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			170 lines
		
	
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
/**
 | 
						|
 * Initialize the Calendar and generate nodes for week headers and days, and
 | 
						|
 * attach event listeners.
 | 
						|
 *
 | 
						|
 * @param {Object} options
 | 
						|
 *        {
 | 
						|
 *          {Number} calViewSize: Number of days to appear on a calendar view
 | 
						|
 *          {Function} getDayString: Transform day number to string
 | 
						|
 *          {Function} getWeekHeaderString: Transform day of week number to string
 | 
						|
 *          {Function} setSelection: Set selection for dateKeeper
 | 
						|
 *        }
 | 
						|
 * @param {Object} context
 | 
						|
 *        {
 | 
						|
 *          {DOMElement} weekHeader
 | 
						|
 *          {DOMElement} daysView
 | 
						|
 *        }
 | 
						|
 */
 | 
						|
function Calendar(options, context) {
 | 
						|
  const DAYS_IN_A_WEEK = 7;
 | 
						|
 | 
						|
  this.context = context;
 | 
						|
  this.state = {
 | 
						|
    days: [],
 | 
						|
    weekHeaders: [],
 | 
						|
    setSelection: options.setSelection,
 | 
						|
    getDayString: options.getDayString,
 | 
						|
    getWeekHeaderString: options.getWeekHeaderString,
 | 
						|
  };
 | 
						|
  this.elements = {
 | 
						|
    weekHeaders: this._generateNodes(DAYS_IN_A_WEEK, context.weekHeader),
 | 
						|
    daysView: this._generateNodes(options.calViewSize, context.daysView),
 | 
						|
  };
 | 
						|
 | 
						|
  this._attachEventListeners();
 | 
						|
}
 | 
						|
 | 
						|
Calendar.prototype = {
 | 
						|
  /**
 | 
						|
   * Set new properties and render them.
 | 
						|
   *
 | 
						|
   * @param {Object} props
 | 
						|
   *        {
 | 
						|
   *          {Boolean} isVisible: Whether or not the calendar is in view
 | 
						|
   *          {Array<Object>} days: Data for days
 | 
						|
   *          {
 | 
						|
   *            {Date} dateObj
 | 
						|
   *            {Number} content
 | 
						|
   *            {Array<String>} classNames
 | 
						|
   *            {Boolean} enabled
 | 
						|
   *          }
 | 
						|
   *          {Array<Object>} weekHeaders: Data for weekHeaders
 | 
						|
   *          {
 | 
						|
   *            {Number} content
 | 
						|
   *            {Array<String>} classNames
 | 
						|
   *          }
 | 
						|
   *        }
 | 
						|
   */
 | 
						|
  setProps(props) {
 | 
						|
    if (props.isVisible) {
 | 
						|
      // Transform the days and weekHeaders array for rendering
 | 
						|
      const days = props.days.map(
 | 
						|
        ({ dateObj, content, classNames, enabled }) => {
 | 
						|
          return {
 | 
						|
            dateObj,
 | 
						|
            textContent: this.state.getDayString(content),
 | 
						|
            className: classNames.join(" "),
 | 
						|
            enabled,
 | 
						|
          };
 | 
						|
        }
 | 
						|
      );
 | 
						|
      const weekHeaders = props.weekHeaders.map(({ content, classNames }) => {
 | 
						|
        return {
 | 
						|
          textContent: this.state.getWeekHeaderString(content),
 | 
						|
          className: classNames.join(" "),
 | 
						|
        };
 | 
						|
      });
 | 
						|
      // Update the DOM nodes states
 | 
						|
      this._render({
 | 
						|
        elements: this.elements.daysView,
 | 
						|
        items: days,
 | 
						|
        prevState: this.state.days,
 | 
						|
      });
 | 
						|
      this._render({
 | 
						|
        elements: this.elements.weekHeaders,
 | 
						|
        items: weekHeaders,
 | 
						|
        prevState: this.state.weekHeaders,
 | 
						|
      });
 | 
						|
      // Update the state to current
 | 
						|
      this.state.days = days;
 | 
						|
      this.state.weekHeaders = weekHeaders;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Render the items onto the DOM nodes
 | 
						|
   * @param  {Object}
 | 
						|
   *         {
 | 
						|
   *           {Array<DOMElement>} elements
 | 
						|
   *           {Array<Object>} items
 | 
						|
   *           {Array<Object>} prevState: state of items from last render
 | 
						|
   *         }
 | 
						|
   */
 | 
						|
  _render({ elements, items, prevState }) {
 | 
						|
    for (let i = 0, l = items.length; i < l; i++) {
 | 
						|
      let el = elements[i];
 | 
						|
 | 
						|
      // Check if state from last render has changed, if so, update the elements
 | 
						|
      if (!prevState[i] || prevState[i].textContent != items[i].textContent) {
 | 
						|
        el.textContent = items[i].textContent;
 | 
						|
      }
 | 
						|
      if (!prevState[i] || prevState[i].className != items[i].className) {
 | 
						|
        el.className = items[i].className;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Generate DOM nodes
 | 
						|
   *
 | 
						|
   * @param  {Number} size: Number of nodes to generate
 | 
						|
   * @param  {DOMElement} context: Element to append the nodes to
 | 
						|
   * @return {Array<DOMElement>}
 | 
						|
   */
 | 
						|
  _generateNodes(size, context) {
 | 
						|
    let frag = document.createDocumentFragment();
 | 
						|
    let refs = [];
 | 
						|
 | 
						|
    for (let i = 0; i < size; i++) {
 | 
						|
      let el = document.createElement("div");
 | 
						|
      el.dataset.id = i;
 | 
						|
      refs.push(el);
 | 
						|
      frag.appendChild(el);
 | 
						|
    }
 | 
						|
    context.appendChild(frag);
 | 
						|
 | 
						|
    return refs;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Handle events
 | 
						|
   * @param  {DOMEvent} event
 | 
						|
   */
 | 
						|
  handleEvent(event) {
 | 
						|
    switch (event.type) {
 | 
						|
      case "click": {
 | 
						|
        if (event.target.parentNode == this.context.daysView) {
 | 
						|
          let targetId = event.target.dataset.id;
 | 
						|
          let targetObj = this.state.days[targetId];
 | 
						|
          if (targetObj.enabled) {
 | 
						|
            this.state.setSelection(targetObj.dateObj);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Attach event listener to daysView
 | 
						|
   */
 | 
						|
  _attachEventListeners() {
 | 
						|
    this.context.daysView.addEventListener("click", this);
 | 
						|
  },
 | 
						|
};
 |