Files
tubestation/toolkit/content/widgets/calendar.js
Cristian Tuns 0a87ef3aaf Backed out 4 changesets (bug 1676068) for causing geckoview failures CLOSED TREE
Backed out changeset ee747c27b9bc (bug 1676068)
Backed out changeset 0876924c66e8 (bug 1676068)
Backed out changeset 30a8292a37ae (bug 1676068)
Backed out changeset 19a0b7a0987c (bug 1676068)
2022-12-01 19:44:47 -05:00

171 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);
},
};