Bug 1320225 - [DateTimeInput] Integration of input type=date picker with input box (part 2). r=mconley
MozReview-Commit-ID: DcwvCSa1ofR
--- a/toolkit/content/widgets/datekeeper.js
+++ b/toolkit/content/widgets/datekeeper.js
@@ -6,35 +6,35 @@
/**
* DateKeeper keeps track of the date states.
*
* @param {Object} date parts
* {
* {Number} year
* {Number} month
- * {Number} date
+ * {Number} day
* }
* {Object} options
* {
* {Number} firstDayOfWeek [optional]
* {Array<Number>} weekends [optional]
* {Number} calViewSize [optional]
* }
*/
-function DateKeeper({ year, month, date }, { firstDayOfWeek = 0, weekends = [0], calViewSize = 42 }) {
+function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0], calViewSize = 42 }) {
this.state = {
firstDayOfWeek, weekends, calViewSize,
dateObj: new Date(0),
years: [],
months: [],
days: []
};
this.state.weekHeaders = this._getWeekHeaders(firstDayOfWeek);
- this._update(year, month, date);
+ this._update(year, month, day);
}
{
const DAYS_IN_A_WEEK = 7,
MONTHS_IN_A_YEAR = 12,
YEAR_VIEW_SIZE = 200,
YEAR_BUFFER_SIZE = 10;
@@ -43,68 +43,68 @@ function DateKeeper({ year, month, date
* Set new date
* @param {Object} date parts
* {
* {Number} year [optional]
* {Number} month [optional]
* {Number} date [optional]
* }
*/
- set({ year = this.state.year, month = this.state.month, date = this.state.date }) {
- this._update(year, month, date);
+ set({ year = this.state.year, month = this.state.month, day = this.state.day }) {
+ this._update(year, month, day);
},
/**
* Set date with value
* @param {Number} value: Date value
*/
setValue(value) {
const dateObj = new Date(value);
this._update(dateObj.getUTCFullYear(), dateObj.getUTCMonth(), dateObj.getUTCDate());
},
/**
- * Set month. Makes sure the date is <= the last day of the month
+ * Set month. Makes sure the day is <= the last day of the month
* @param {Number} month
*/
setMonth(month) {
const lastDayOfMonth = this._newUTCDate(this.state.year, month + 1, 0).getUTCDate();
- this._update(this.state.year, month, Math.min(this.state.date, lastDayOfMonth));
+ this._update(this.state.year, month, Math.min(this.state.day, lastDayOfMonth));
},
/**
- * Set year. Makes sure the date is <= the last day of the month
+ * Set year. Makes sure the day is <= the last day of the month
* @param {Number} year
*/
setYear(year) {
const lastDayOfMonth = this._newUTCDate(year, this.state.month + 1, 0).getUTCDate();
- this._update(year, this.state.month, Math.min(this.state.date, lastDayOfMonth));
+ this._update(year, this.state.month, Math.min(this.state.day, lastDayOfMonth));
},
/**
- * Set month by offset. Makes sure the date is <= the last day of the month
+ * Set month by offset. Makes sure the day is <= the last day of the month
* @param {Number} offset
*/
setMonthByOffset(offset) {
const lastDayOfMonth = this._newUTCDate(this.state.year, this.state.month + offset + 1, 0).getUTCDate();
- this._update(this.state.year, this.state.month + offset, Math.min(this.state.date, lastDayOfMonth));
+ this._update(this.state.year, this.state.month + offset, Math.min(this.state.day, lastDayOfMonth));
},
/**
* Update the states.
* @param {Number} year [description]
* @param {Number} month [description]
- * @param {Number} date [description]
+ * @param {Number} day [description]
*/
- _update(year, month, date) {
+ _update(year, month, day) {
// Use setUTCFullYear so that year 99 doesn't get parsed as 1999
- this.state.dateObj.setUTCFullYear(year, month, date);
+ this.state.dateObj.setUTCFullYear(year, month, day);
this.state.year = this.state.dateObj.getUTCFullYear();
this.state.month = this.state.dateObj.getUTCMonth();
- this.state.date = this.state.dateObj.getUTCDate();
+ this.state.day = this.state.dateObj.getUTCDate();
},
/**
* Generate the array of months
* @return {Array<Object>}
* {
* {Number} value: Month in int
* {Boolean} enabled
@@ -196,24 +196,24 @@ function DateKeeper({ year, month, date
* @return {Array<Object>}
* {
* {Number} textContent
* {Array<String>} classNames
* }
*/
_getWeekHeaders(firstDayOfWeek) {
let headers = [];
- let day = firstDayOfWeek;
+ let dayOfWeek = firstDayOfWeek;
for (let i = 0; i < DAYS_IN_A_WEEK; i++) {
headers.push({
- textContent: day % DAYS_IN_A_WEEK,
- classNames: this.state.weekends.includes(day % DAYS_IN_A_WEEK) ? ["weekend"] : []
+ textContent: dayOfWeek % DAYS_IN_A_WEEK,
+ classNames: this.state.weekends.includes(dayOfWeek % DAYS_IN_A_WEEK) ? ["weekend"] : []
});
- day++;
+ dayOfWeek++;
}
return headers;
},
/**
* Get the first day on a calendar month
* @param {Date} dateObj
* @param {Number} firstDayOfWeek
--- a/toolkit/content/widgets/datepicker.js
+++ b/toolkit/content/widgets/datepicker.js
@@ -32,23 +32,23 @@ function DatePicker(context) {
/*
* Set initial date picker states.
*/
_setDefaultState() {
const now = new Date();
const { year = now.getFullYear(),
month = now.getMonth(),
- date = now.getDate(),
+ day = now.getDate(),
locale } = this.props;
// TODO: Use calendar info API to get first day of week & weekends
// (Bug 1287503)
const dateKeeper = new DateKeeper({
- year, month, date
+ year, month, day
}, {
calViewSize: CAL_VIEW_SIZE,
firstDayOfWeek: 0,
weekends: [0]
});
this.state = {
dateKeeper,
@@ -63,16 +63,17 @@ function DatePicker(context) {
setValue: ({ dateValue, selectionValue }) => {
dateKeeper.setValue(dateValue);
this.state.selectionValue = selectionValue;
this.state.isYearSet = true;
this.state.isMonthSet = true;
this.state.isDateSet = true;
this._update();
this._dispatchState();
+ this._closePopup();
},
setYear: year => {
dateKeeper.setYear(year);
this.state.isYearSet = true;
this._update();
this._dispatchState();
},
setMonth: month => {
@@ -144,32 +145,41 @@ function DatePicker(context) {
});
isMonthPickerVisible ?
this.context.monthYearView.classList.remove("hidden") :
this.context.monthYearView.classList.add("hidden");
},
/**
+ * Use postMessage to close the picker.
+ */
+ _closePopup() {
+ window.postMessage({
+ name: "ClosePopup"
+ }, "*");
+ },
+
+ /**
* Use postMessage to pass the state of picker to the panel.
*/
_dispatchState() {
- const { year, month, date } = this.state.dateKeeper.state;
- const { isYearSet, isMonthSet, isDateSet } = this.state;
+ const { year, month, day } = this.state.dateKeeper.state;
+ const { isYearSet, isMonthSet, isDaySet } = this.state;
// The panel is listening to window for postMessage event, so we
// do postMessage to itself to send data to input boxes.
window.postMessage({
- name: "DatePickerPopupChanged",
+ name: "PickerPopupChanged",
detail: {
year,
month,
- date,
+ day,
isYearSet,
isMonthSet,
- isDateSet
+ isDaySet
}
}, "*");
},
/**
* Attach event listeners
*/
_attachEventListeners() {
@@ -216,49 +226,53 @@ function DatePicker(context) {
/**
* Handle postMessage events.
*
* @param {Event} event
*/
handleMessage(event) {
switch (event.data.name) {
- case "DatePickerSetValue": {
+ case "PickerSetValue": {
this.set(event.data.detail);
break;
}
- case "DatePickerInit": {
+ case "PickerInit": {
this.init(event.data.detail);
break;
}
}
},
/**
* Set the date state and update the components with the new state.
*
* @param {Object} dateState
* {
* {Number} year [optional]
* {Number} month [optional]
* {Number} date [optional]
* }
*/
- set(dateState) {
- if (dateState.year != undefined) {
+ set({ year, month, day }) {
+ const { dateKeeper } = this.state;
+
+ if (year != undefined) {
this.state.isYearSet = true;
}
- if (dateState.month != undefined) {
+ if (month != undefined) {
this.state.isMonthSet = true;
}
- if (dateState.date != undefined) {
- this.state.isDateSet = true;
+ if (day != undefined) {
+ this.state.isDaySet = true;
}
- this.state.dateKeeper.set(dateState);
+ dateKeeper.set({
+ year, month, day
+ });
this._update();
}
};
/**
* MonthYear is a component that handles the month & year spinners
*
* @param {Object} options
--- a/toolkit/content/widgets/datetimepopup.xml
+++ b/toolkit/content/widgets/datetimepopup.xml
@@ -15,16 +15,18 @@
<stylesheet src="chrome://global/skin/datetimepopup.css"/>
</resources>
<implementation>
<field name="dateTimePopupFrame">
this.querySelector("#dateTimePopupFrame");
</field>
<field name="TIME_PICKER_WIDTH" readonly="true">"12em"</field>
<field name="TIME_PICKER_HEIGHT" readonly="true">"21em"</field>
+ <field name="DATE_PICKER_WIDTH" readonly="true">"23.1em"</field>
+ <field name="DATE_PICKER_HEIGHT" readonly="true">"20.7em"</field>
<method name="loadPicker">
<parameter name="type"/>
<parameter name="detail"/>
<body><![CDATA[
this.hidden = false;
this.type = type;
this.pickerState = {};
// TODO: Resize picker according to content zoom level
@@ -33,67 +35,103 @@
case "time": {
this.detail = detail;
this.dateTimePopupFrame.addEventListener("load", this, true);
this.dateTimePopupFrame.setAttribute("src", "chrome://global/content/timepicker.xhtml");
this.dateTimePopupFrame.style.width = this.TIME_PICKER_WIDTH;
this.dateTimePopupFrame.style.height = this.TIME_PICKER_HEIGHT;
break;
}
+ case "date": {
+ this.detail = detail;
+ this.dateTimePopupFrame.addEventListener("load", this, true);
+ this.dateTimePopupFrame.setAttribute("src", "chrome://global/content/datepicker.xhtml");
+ this.dateTimePopupFrame.style.width = this.DATE_PICKER_WIDTH;
+ this.dateTimePopupFrame.style.height = this.DATE_PICKER_HEIGHT;
+ break;
+ }
}
]]></body>
</method>
<method name="closePicker">
<body><![CDATA[
this.hidden = true;
this.setInputBoxValue(true);
this.pickerState = {};
this.type = undefined;
this.dateTimePopupFrame.removeEventListener("load", this, true);
- this.dateTimePopupFrame.contentDocument.removeEventListener("TimePickerPopupChanged", this, false);
+ this.dateTimePopupFrame.contentDocument.removeEventListener("message", this, false);
this.dateTimePopupFrame.setAttribute("src", "");
]]></body>
</method>
<method name="setPopupValue">
<parameter name="data"/>
<body><![CDATA[
switch (this.type) {
case "time": {
this.postMessageToPicker({
- name: "TimePickerSetValue",
+ name: "PickerSetValue",
detail: data.value
});
break;
}
+ case "date": {
+ const { year, month, day } = data.value;
+ this.postMessageToPicker({
+ name: "PickerSetValue",
+ detail: {
+ year,
+ // Month value from input box starts from 1 instead of 0
+ month: month == undefined ? undefined : month - 1,
+ day
+ }
+ });
+ break;
+ }
}
]]></body>
</method>
<method name="initPicker">
<parameter name="detail"/>
<body><![CDATA[
+ const locale = Components.classes["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).getSelectedLocale("global");
+
switch (this.type) {
case "time": {
const { hour, minute } = detail.value;
const format = detail.format || "12";
- const locale = Components.classes["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).getSelectedLocale("global");
this.postMessageToPicker({
- name: "TimePickerInit",
+ name: "PickerInit",
detail: {
hour,
minute,
format,
locale,
min: detail.min,
max: detail.max,
step: detail.step,
}
});
break;
}
+ case "date": {
+ const { year, month, day } = detail.value;
+ this.postMessageToPicker({
+ name: "PickerInit",
+ detail: {
+ year,
+ // Month value from input box starts from 1 instead of 0
+ month: month == undefined ? undefined : month - 1,
+ day,
+ locale
+ }
+ });
+ break;
+ }
}
]]></body>
</method>
<method name="setInputBoxValue">
<parameter name="passAllValues"/>
<body><![CDATA[
/**
* @param {Boolean} passAllValues: Pass spinner values regardless if they've been set/changed or not
@@ -107,32 +145,47 @@
} else {
this.sendPickerValueChanged({
hour: isHourSet || isDayPeriodSet ? hour : undefined,
minute: isMinuteSet ? minute : undefined
});
}
break;
}
+ case "date": {
+ this.sendPickerValueChanged(this.pickerState);
+ break;
+ }
}
]]></body>
</method>
<method name="sendPickerValueChanged">
<parameter name="value"/>
<body><![CDATA[
switch (this.type) {
case "time": {
this.dispatchEvent(new CustomEvent("DateTimePickerValueChanged", {
detail: {
hour: value.hour,
minute: value.minute
}
}));
break;
}
+ case "date": {
+ this.dispatchEvent(new CustomEvent("DateTimePickerValueChanged", {
+ detail: {
+ year: value.year,
+ // Month value from input box starts from 1 instead of 0
+ month: value.month == undefined ? undefined : value.month + 1,
+ day: value.day
+ }
+ }));
+ break;
+ }
}
]]></body>
</method>
<method name="handleEvent">
<parameter name="aEvent"/>
<body><![CDATA[
switch (aEvent.type) {
case "load": {
@@ -150,21 +203,25 @@
<method name="handleMessage">
<parameter name="aEvent"/>
<body><![CDATA[
if (!this.dateTimePopupFrame.contentDocument.nodePrincipal.isSystemPrincipal) {
return;
}
switch (aEvent.data.name) {
- case "TimePickerPopupChanged": {
+ case "PickerPopupChanged": {
this.pickerState = aEvent.data.detail;
this.setInputBoxValue();
break;
}
+ case "ClosePopup": {
+ this.closePicker();
+ break;
+ }
}
]]></body>
</method>
<method name="postMessageToPicker">
<parameter name="data"/>
<body><![CDATA[
if (this.dateTimePopupFrame.contentDocument.nodePrincipal.isSystemPrincipal) {
this.dateTimePopupFrame.contentWindow.postMessage(data, "*");
--- a/toolkit/content/widgets/timepicker.js
+++ b/toolkit/content/widgets/timepicker.js
@@ -199,17 +199,17 @@ function TimePicker(context) {
* Dispatch CustomEvent to pass the state of picker to the panel.
*/
_dispatchState() {
const { hour, minute } = this.state.timeKeeper;
const { isHourSet, isMinuteSet, isDayPeriodSet } = this.state;
// The panel is listening to window for postMessage event, so we
// do postMessage to itself to send data to input boxes.
window.postMessage({
- name: "TimePickerPopupChanged",
+ name: "PickerPopupChanged",
detail: {
hour,
minute,
isHourSet,
isMinuteSet,
isDayPeriodSet
}
}, "*");
@@ -241,21 +241,21 @@ function TimePicker(context) {
/**
* Handle postMessage events.
*
* @param {Event} event
*/
handleMessage(event) {
switch (event.data.name) {
- case "TimePickerSetValue": {
+ case "PickerSetValue": {
this.set(event.data.detail);
break;
}
- case "TimePickerInit": {
+ case "PickerInit": {
this.init(event.data.detail);
break;
}
}
},
/**
* Set the time state and update the components with the new state.