Skip to content

Commit e0ad87a

Browse files
david-loeemilio
authored andcommitted
Bug 1726108 - Combining date and time picker to datetime picker. r=emilio,desktop-theme-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D256445
1 parent 55dc8c8 commit e0ad87a

File tree

4 files changed

+187
-126
lines changed

4 files changed

+187
-126
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- This Source Code Form is subject to the terms of the Mozilla Public
3+
- License, v. 2.0. If a copy of the MPL was not distributed with this
4+
- file, You can obtain one at https://mozilla.org/MPL/2.0/. -->
5+
6+
<!DOCTYPE html [ <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
7+
%htmlDTD; ]>
8+
<html
9+
xmlns="http://www.w3.org/1999/xhtml"
10+
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
11+
>
12+
<head>
13+
<meta http-equiv="Content-Security-Policy" content="default-src chrome:;" />
14+
15+
<link
16+
rel="stylesheet"
17+
href="chrome://global/skin/datetimeinputpickers.css"
18+
/>
19+
<link rel="localization" href="toolkit/global/datepicker.ftl" />
20+
<script src="chrome://global/content/bindings/datekeeper.js"></script>
21+
<script src="chrome://global/content/bindings/spinner.js"></script>
22+
<script src="chrome://global/content/bindings/calendar.js"></script>
23+
<script src="chrome://global/content/bindings/datepicker.js"></script>
24+
25+
<script src="chrome://global/content/bindings/timekeeper.js"></script>
26+
<script src="chrome://global/content/bindings/timepicker.js"></script>
27+
</head>
28+
<body>
29+
<div id="datetime-picker">
30+
<div
31+
id="date-picker"
32+
class="picker"
33+
role="dialog"
34+
data-l10n-id="date-picker-label"
35+
aria-modal="true"
36+
>
37+
<div class="calendar-container">
38+
<div class="month-year-nav" data-l10n-id="date-spinner-label">
39+
<button class="prev" data-l10n-id="date-picker-previous" />
40+
<div class="month-year-container">
41+
<button
42+
class="month-year"
43+
id="month-year-label"
44+
aria-live="polite"
45+
/>
46+
</div>
47+
<button class="next" data-l10n-id="date-picker-next" />
48+
<div class="month-year-view"></div>
49+
</div>
50+
<table role="grid" aria-labelledby="month-year-label">
51+
<thead class="week-header"></thead>
52+
<tbody class="days-view"></tbody>
53+
</table>
54+
</div>
55+
<button id="clear-button" data-l10n-id="date-picker-clear-button" />
56+
</div>
57+
58+
<div id="time-picker" class="picker"></div>
59+
</div>
60+
<template id="spinner-template">
61+
<div class="spinner-container">
62+
<button class="up" />
63+
<div class="spinner"></div>
64+
<button class="down" />
65+
</div>
66+
</template>
67+
</body>
68+
</html>

toolkit/content/jar.mn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ toolkit.jar:
6363
content/global/resetProfileProgress.xhtml
6464
content/global/TopLevelVideoDocument.js
6565
content/global/timepicker.xhtml
66+
content/global/datetimepicker.xhtml
6667
content/global/treeUtils.js
6768
#ifndef MOZ_FENNEC
6869
content/global/viewZoomOverlay.js

toolkit/modules/DateTimePickerPanel.sys.mjs

Lines changed: 112 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export var DateTimePickerPanel = class {
1010
this.TIME_PICKER_HEIGHT = "22em";
1111
this.DATE_PICKER_WIDTH = "24em";
1212
this.DATE_PICKER_HEIGHT = "27em";
13+
this.DATETIME_PICKER_WIDTH = "40em";
14+
this.DATETIME_PICKER_HEIGHT = "27em";
1315
}
1416

1517
get dateTimePopupFrame() {
@@ -23,17 +25,14 @@ export var DateTimePickerPanel = class {
2325
}
2426

2527
openPicker(type, rect, detail) {
26-
if (type == "datetime-local") {
27-
type = "date";
28-
}
29-
this.type = type;
3028
this.pickerState = {};
3129
// TODO: Resize picker according to content zoom level
3230
this.element.style.fontSize = "10px";
31+
this.type = type;
32+
this.detail = detail;
33+
this.dateTimePopupFrame.addEventListener("load", this, true);
3334
switch (type) {
3435
case "time": {
35-
this.detail = detail;
36-
this.dateTimePopupFrame.addEventListener("load", this, true);
3736
this.dateTimePopupFrame.setAttribute(
3837
"src",
3938
"chrome://global/content/timepicker.xhtml"
@@ -43,8 +42,6 @@ export var DateTimePickerPanel = class {
4342
break;
4443
}
4544
case "date": {
46-
this.detail = detail;
47-
this.dateTimePopupFrame.addEventListener("load", this, true);
4845
this.dateTimePopupFrame.setAttribute(
4946
"src",
5047
"chrome://global/content/datepicker.xhtml"
@@ -53,6 +50,15 @@ export var DateTimePickerPanel = class {
5350
this.dateTimePopupFrame.style.height = this.DATE_PICKER_HEIGHT;
5451
break;
5552
}
53+
case "datetime-local": {
54+
this.dateTimePopupFrame.setAttribute(
55+
"src",
56+
"chrome://global/content/datetimepicker.xhtml"
57+
);
58+
this.dateTimePopupFrame.style.width = this.DATETIME_PICKER_WIDTH;
59+
this.dateTimePopupFrame.style.height = this.DATETIME_PICKER_HEIGHT;
60+
break;
61+
}
5662
}
5763
this.element.openPopupAtScreenRect(
5864
"after_start",
@@ -81,28 +87,13 @@ export var DateTimePickerPanel = class {
8187
}
8288

8389
setPopupValue(data) {
84-
switch (this.type) {
85-
case "time": {
86-
this.postMessageToPicker({
87-
name: "PickerSetValue",
88-
detail: data.value,
89-
});
90-
break;
91-
}
92-
case "date": {
93-
const { year, month, day } = data.value;
94-
this.postMessageToPicker({
95-
name: "PickerSetValue",
96-
detail: {
97-
year,
98-
// Month value from input box starts from 1 instead of 0
99-
month: month == undefined ? undefined : month - 1,
100-
day,
101-
},
102-
});
103-
break;
104-
}
105-
}
90+
const detail = data.value;
91+
// Month value from input box starts from 1 instead of 0
92+
detail.month = detail.month == undefined ? undefined : detail.month - 1;
93+
this.postMessageToPicker({
94+
name: "PickerSetValue",
95+
detail,
96+
});
10697
}
10798

10899
initPicker(detail) {
@@ -121,125 +112,120 @@ export var DateTimePickerPanel = class {
121112

122113
const dir = Services.locale.isAppLocaleRTL ? "rtl" : "ltr";
123114

124-
switch (this.type) {
125-
case "time": {
126-
const { hour, minute } = detail.value;
127-
const format = detail.format || "12";
128-
129-
this.postMessageToPicker({
130-
name: "PickerInit",
131-
detail: {
132-
hour,
133-
minute,
134-
format,
135-
locale,
136-
min: detail.min,
137-
max: detail.max,
138-
step: detail.step,
139-
},
140-
});
141-
break;
142-
}
143-
case "date": {
144-
const { year, month, day } = detail.value;
145-
const { firstDayOfWeek, weekends } = this.getCalendarInfo(locale);
115+
const { year, month, day, hour, minute } = detail.value;
116+
const flattenDetail = {
117+
year,
118+
// Month value from input box starts from 1 instead of 0
119+
month: month == undefined ? undefined : month - 1,
120+
day,
121+
hour,
122+
minute,
123+
locale,
124+
dir,
125+
format: detail.format || "12",
126+
min: detail.min,
127+
max: detail.max,
128+
step: detail.step,
129+
stepBase: detail.stepBase,
130+
};
146131

147-
const monthDisplayNames = new Services.intl.DisplayNames(locale, {
148-
type: "month",
149-
style: "short",
150-
calendar: "gregory",
151-
});
152-
const monthStrings = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(
153-
month => monthDisplayNames.of(month)
154-
);
132+
if (this.type !== "time") {
133+
const { firstDayOfWeek, weekends } = this.getCalendarInfo(locale);
155134

156-
const weekdayDisplayNames = new Services.intl.DisplayNames(locale, {
157-
type: "weekday",
158-
style: "abbreviated",
159-
calendar: "gregory",
160-
});
161-
const weekdayStrings = [
162-
// Weekdays starting Sunday (7) to Saturday (6).
163-
7, 1, 2, 3, 4, 5, 6,
164-
].map(weekday => weekdayDisplayNames.of(weekday));
135+
const monthDisplayNames = new Services.intl.DisplayNames(locale, {
136+
type: "month",
137+
style: "short",
138+
calendar: "gregory",
139+
});
140+
const monthStrings = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(
141+
monthNumber => monthDisplayNames.of(monthNumber)
142+
);
165143

166-
this.postMessageToPicker({
167-
name: "PickerInit",
168-
detail: {
169-
year,
170-
// Month value from input box starts from 1 instead of 0
171-
month: month == undefined ? undefined : month - 1,
172-
day,
173-
firstDayOfWeek,
174-
weekends,
175-
monthStrings,
176-
weekdayStrings,
177-
locale,
178-
dir,
179-
min: detail.min,
180-
max: detail.max,
181-
step: detail.step,
182-
stepBase: detail.stepBase,
183-
},
184-
});
185-
break;
186-
}
144+
const weekdayDisplayNames = new Services.intl.DisplayNames(locale, {
145+
type: "weekday",
146+
style: "abbreviated",
147+
calendar: "gregory",
148+
});
149+
const weekdayStrings = [
150+
// Weekdays starting Sunday (7) to Saturday (6).
151+
7, 1, 2, 3, 4, 5, 6,
152+
].map(weekday => weekdayDisplayNames.of(weekday));
153+
Object.assign(flattenDetail, {
154+
firstDayOfWeek,
155+
weekends,
156+
monthStrings,
157+
weekdayStrings,
158+
});
187159
}
160+
this.postMessageToPicker({
161+
name: "PickerInit",
162+
detail: flattenDetail,
163+
});
188164
}
189165

190166
/**
191167
* @param {Boolean} passAllValues: Pass spinner values regardless if they've been set/changed or not
192168
*/
193169
setInputBoxValue(passAllValues) {
194-
switch (this.type) {
195-
case "time": {
196-
const { hour, minute, isHourSet, isMinuteSet, isDayPeriodSet } =
197-
this.pickerState;
198-
const isAnyValueSet = isHourSet || isMinuteSet || isDayPeriodSet;
199-
if (passAllValues && isAnyValueSet) {
200-
this.sendPickerValueChanged({ hour, minute });
201-
} else {
202-
this.sendPickerValueChanged({
203-
hour: isHourSet || isDayPeriodSet ? hour : undefined,
204-
minute: isMinuteSet ? minute : undefined,
205-
});
206-
}
207-
break;
208-
}
209-
case "date": {
210-
this.sendPickerValueChanged(this.pickerState);
211-
break;
170+
const value = {
171+
year: this.pickerState.year,
172+
month: this.pickerState.month,
173+
day: this.pickerState.day,
174+
hour: this.pickerState.hour,
175+
minute: this.pickerState.minute,
176+
};
177+
if (this.type !== "date") {
178+
const isNoValueSet =
179+
this.pickerState.isHourSet ||
180+
this.pickerState.isMinuteSet ||
181+
this.pickerState.isDayPeriodSet;
182+
if (!passAllValues || isNoValueSet) {
183+
value.hour =
184+
this.pickerState.isHourSet || this.pickerState.isDayPeriodSet
185+
? value.hour
186+
: undefined;
187+
value.minute = this.pickerState.isMinuteSet ? value.minute : undefined;
212188
}
213189
}
190+
this.sendPickerValueChanged(value);
214191
}
215192

216193
sendPickerValueChanged(value) {
194+
let detail = {};
217195
switch (this.type) {
218196
case "time": {
219-
this.element.dispatchEvent(
220-
new CustomEvent("DateTimePickerValueChanged", {
221-
detail: {
222-
hour: value.hour,
223-
minute: value.minute,
224-
},
225-
})
226-
);
197+
detail = {
198+
hour: value.hour,
199+
minute: value.minute,
200+
};
227201
break;
228202
}
229203
case "date": {
230-
this.element.dispatchEvent(
231-
new CustomEvent("DateTimePickerValueChanged", {
232-
detail: {
233-
year: value.year,
234-
// Month value from input box starts from 1 instead of 0
235-
month: value.month == undefined ? undefined : value.month + 1,
236-
day: value.day,
237-
},
238-
})
239-
);
204+
detail = {
205+
year: value.year,
206+
// Month value from input box starts from 1 instead of 0
207+
month: value.month == undefined ? undefined : value.month + 1,
208+
day: value.day,
209+
};
210+
break;
211+
}
212+
case "datetime-local": {
213+
detail = {
214+
year: value.year,
215+
// Month value from input box starts from 1 instead of 0
216+
month: value.month == undefined ? undefined : value.month + 1,
217+
day: value.day,
218+
hour: value.hour,
219+
minute: value.minute,
220+
};
240221
break;
241222
}
242223
}
224+
this.element.dispatchEvent(
225+
new CustomEvent("DateTimePickerValueChanged", {
226+
detail,
227+
})
228+
);
243229
}
244230

245231
getCalendarInfo(locale) {

toolkit/themes/shared/datetimeinputpickers.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,9 @@ button.month-year.active {
380380
.spacer {
381381
width: var(--day-period-spacing-width);
382382
}
383+
384+
#datetime-picker {
385+
display: flex;
386+
align-items: flex-start;
387+
gap: 1em;
388+
}

0 commit comments

Comments
 (0)