FEATURE: local dates range on click (#14355)

This PR is introducing 2 changes.
1. Date popup is displayed on click instead on hover
2. If the range is given then the whole range is always displayed for both startDate and endDate
3. For range, short time is displayed for end if the range is < 24 hours
This commit is contained in:
Krzysztof Kotlarek
2021-09-20 09:23:18 +10:00
committed by GitHub
parent e316467169
commit 82b7e34f30
3 changed files with 139 additions and 27 deletions

View File

@ -39,6 +39,11 @@ export function applyLocalDates(dates, siteSettings) {
function buildOptionsFromElement(element, siteSettings) { function buildOptionsFromElement(element, siteSettings) {
const opts = {}; const opts = {};
const dataset = element.dataset; const dataset = element.dataset;
if (_rangeElements(element).length === 2) {
opts.duration = _calculateDuration(element);
}
opts.time = dataset.time; opts.time = dataset.time;
opts.date = dataset.date; opts.date = dataset.date;
opts.recurring = dataset.recurring; opts.recurring = dataset.recurring;
@ -57,6 +62,12 @@ function buildOptionsFromElement(element, siteSettings) {
return opts; return opts;
} }
function _rangeElements(element) {
return Array.from(element.parentElement.children).filter(
(span) => span.dataset.date
);
}
function initializeDiscourseLocalDates(api) { function initializeDiscourseLocalDates(api) {
const siteSettings = api.container.lookup("site-settings:main"); const siteSettings = api.container.lookup("site-settings:main");
@ -114,7 +125,7 @@ function buildHtmlPreview(element, siteSettings) {
const dateTimeNode = document.createElement("span"); const dateTimeNode = document.createElement("span");
dateTimeNode.classList.add("date-time"); dateTimeNode.classList.add("date-time");
dateTimeNode.innerText = preview.formated; dateTimeNode.innerHTML = preview.formated;
previewNode.appendChild(dateTimeNode); previewNode.appendChild(dateTimeNode);
return previewNode; return previewNode;
@ -127,6 +138,20 @@ function buildHtmlPreview(element, siteSettings) {
return previewsNode.outerHTML; return previewsNode.outerHTML;
} }
function _calculateDuration(element) {
const [startDataset, endDataset] = _rangeElements(element).map(
(dateElement) => dateElement.dataset
);
const startDateTime = moment(
`${startDataset.date} ${startDataset.time || ""}`
);
const endDateTime = moment(`${endDataset.date} ${endDataset.time || ""}`);
const duration = endDateTime.diff(startDateTime, "minutes");
// negative duration is used when we calculate difference for end date from range
return element.dataset === startDataset ? duration : -duration;
}
export default { export default {
name: "discourse-local-dates", name: "discourse-local-dates",
@ -138,9 +163,13 @@ export default {
const siteSettings = owner.lookup("site-settings:main"); const siteSettings = owner.lookup("site-settings:main");
if (event?.target?.classList?.contains("discourse-local-date")) { if (event?.target?.classList?.contains("discourse-local-date")) {
showPopover(event, { if ($(document.getElementById("d-popover"))[0]) {
htmlContent: buildHtmlPreview(event.target, siteSettings), hidePopover(event);
}); } else {
showPopover(event, {
htmlContent: buildHtmlPreview(event.target, siteSettings),
});
}
} }
}, },
@ -155,7 +184,6 @@ export default {
router.on("routeWillChange", hidePopover); router.on("routeWillChange", hidePopover);
window.addEventListener("click", this.showDatePopover); window.addEventListener("click", this.showDatePopover);
window.addEventListener("mouseover", this.showDatePopover);
window.addEventListener("mouseout", this.hideDatePopover); window.addEventListener("mouseout", this.hideDatePopover);
const siteSettings = container.lookup("site-settings:main"); const siteSettings = container.lookup("site-settings:main");
@ -174,7 +202,6 @@ export default {
teardown() { teardown() {
window.removeEventListener("click", this.showDatePopover); window.removeEventListener("click", this.showDatePopover);
window.removeEventListener("mouseover", this.showDatePopover);
window.removeEventListener("mouseout", this.hideDatePopover); window.removeEventListener("mouseout", this.hideDatePopover);
}, },
}; };

View File

@ -1,9 +1,15 @@
import DateWithZoneHelper from "./date-with-zone-helper"; import DateWithZoneHelper from "./date-with-zone-helper";
import I18n from "I18n"; import I18n from "I18n";
import { renderIcon } from "discourse-common/lib/icon-library";
const TIME_FORMAT = "LLL"; const DATETIME_FORMAT = "LLL";
const DATE_FORMAT = "LL"; const DATE_FORMAT = "LL";
const FULL_DATETIME_FORMAT = "LLLL";
const TIME_FORMAT = "h:mm A";
const DAY_OF_THE_WEEK_FORMAT = "dddd";
const RANGE_SEPARATOR = "→"; const RANGE_SEPARATOR = "→";
const TIME_ICON = "clock";
const SHORT_FORMAT_DAYS_PERIOD = 1;
export default class LocalDateBuilder { export default class LocalDateBuilder {
constructor(params = {}, localTimezone) { constructor(params = {}, localTimezone) {
@ -17,8 +23,9 @@ export default class LocalDateBuilder {
this.calendar = this.calendar =
typeof params.calendar === "undefined" ? true : params.calendar; typeof params.calendar === "undefined" ? true : params.calendar;
this.displayedTimezone = params.displayedTimezone; this.displayedTimezone = params.displayedTimezone;
this.format = params.format || (this.time ? TIME_FORMAT : DATE_FORMAT); this.format = params.format || (this.time ? DATETIME_FORMAT : DATE_FORMAT);
this.countdown = params.countdown; this.countdown = params.countdown;
this.duration = params.duration;
this.localTimezone = localTimezone; this.localTimezone = localTimezone;
} }
@ -95,7 +102,8 @@ export default class LocalDateBuilder {
localDate.timezone, localDate.timezone,
this.localTimezone this.localTimezone
), ),
this.time this.time,
this.duration
), ),
}); });
@ -126,7 +134,8 @@ export default class LocalDateBuilder {
localDate.timezone, localDate.timezone,
timezone timezone
), ),
this.time this.time,
this.duration
), ),
}); });
}); });
@ -148,19 +157,56 @@ export default class LocalDateBuilder {
); );
} }
_createDateTimeRange(startRange, time) { _createDateTimeRange(startRange, time, duration) {
const [startDate, endDate] = this._calculateDatesForRange(
startRange,
time,
duration
);
let formatElements = [
startDate.format(`${DAY_OF_THE_WEEK_FORMAT}, ${DATE_FORMAT}`),
this._optionalTimeIcon(startDate, endDate),
startDate.format(TIME_FORMAT),
];
if (endDate) {
formatElements = formatElements.concat([
RANGE_SEPARATOR,
endDate.format(this._endDateFormat(startDate, endDate)),
]);
}
return formatElements.filter(Boolean).join(" ");
}
_shortFormat(startDate, endDate) {
return (
endDate.datetime.diff(startDate.datetime, "days") <
SHORT_FORMAT_DAYS_PERIOD
);
}
_optionalTimeIcon(startDate, endDate) {
if (!endDate || this._shortFormat(startDate, endDate)) {
return `<br />${renderIcon("string", TIME_ICON)}`;
}
}
_endDateFormat(startDate, endDate) {
return this._shortFormat(startDate, endDate)
? TIME_FORMAT
: FULL_DATETIME_FORMAT;
}
_calculateDatesForRange(date, time, duration) {
// if a time has been given we do not attempt to automatically create a range // if a time has been given we do not attempt to automatically create a range
// instead we show only one date with a format showing the time // instead we show only one date with a format showing the time
if (time) { if (time && !duration) {
return startRange.format(TIME_FORMAT); return [date];
} else {
const endRange = startRange.add(24, "hours");
return [
startRange.format("LLLL"),
RANGE_SEPARATOR,
endRange.format("LLLL"),
].join(" ");
} }
const dates = [
date,
duration ? date.add(duration, "minutes") : date.add(24, "hours"),
];
return duration < 0 ? dates.reverse() : dates;
} }
_applyFormatting(localDate, displayedTimezone) { _applyFormatting(localDate, displayedTimezone) {

View File

@ -446,6 +446,38 @@ test("previews", function (assert) {
); );
}); });
freezeTime({ date: "2020-03-22", timezone: PARIS }, () => {
assert.buildsCorrectDate(
{ duration: 90, timezone: PARIS, timezones: [PARIS] },
{
previews: [
{
current: true,
formated:
'Sunday, March 22, 2020 <br /><svg class=\'fa d-icon d-icon-clock svg-icon svg-string\' xmlns="http://www.w3.org/2000/svg"><use xlink:href="#clock" /></svg> 12:00 AM → 1:30 AM',
timezone: "Paris",
},
],
}
);
});
freezeTime({ date: "2020-03-22", timezone: PARIS }, () => {
assert.buildsCorrectDate(
{ duration: 1440, timezone: PARIS, timezones: [PARIS] },
{
previews: [
{
current: true,
formated:
"Sunday, March 22, 2020 12:00 AM → Monday, March 23, 2020 12:00 AM",
timezone: "Paris",
},
],
}
);
});
freezeTime({ date: "2020-03-22", timezone: PARIS }, () => { freezeTime({ date: "2020-03-22", timezone: PARIS }, () => {
assert.buildsCorrectDate( assert.buildsCorrectDate(
{ time: "11:34", timezone: PARIS, timezones: [PARIS] }, { time: "11:34", timezone: PARIS, timezones: [PARIS] },
@ -453,7 +485,8 @@ test("previews", function (assert) {
previews: [ previews: [
{ {
current: true, current: true,
formated: "March 22, 2020 11:34 AM", formated:
'Sunday, March 22, 2020 <br /><svg class=\'fa d-icon d-icon-clock svg-icon svg-string\' xmlns="http://www.w3.org/2000/svg"><use xlink:href="#clock" /></svg> 11:34 AM',
timezone: "Paris", timezone: "Paris",
}, },
], ],
@ -508,19 +541,23 @@ test("previews", function (assert) {
previews: [ previews: [
{ {
current: true, current: true,
formated: "April 7, 2020 2:54 PM", formated:
'Tuesday, April 7, 2020 <br /><svg class=\'fa d-icon d-icon-clock svg-icon svg-string\' xmlns="http://www.w3.org/2000/svg"><use xlink:href="#clock" /></svg> 2:54 PM',
timezone: "Paris", timezone: "Paris",
}, },
{ {
formated: "April 7, 2020 1:54 PM", formated:
'Tuesday, April 7, 2020 <br /><svg class=\'fa d-icon d-icon-clock svg-icon svg-string\' xmlns="http://www.w3.org/2000/svg"><use xlink:href="#clock" /></svg> 1:54 PM',
timezone: "London", timezone: "London",
}, },
{ {
formated: "April 7, 2020 1:54 PM", formated:
'Tuesday, April 7, 2020 <br /><svg class=\'fa d-icon d-icon-clock svg-icon svg-string\' xmlns="http://www.w3.org/2000/svg"><use xlink:href="#clock" /></svg> 1:54 PM',
timezone: "Lagos", timezone: "Lagos",
}, },
{ {
formated: "April 7, 2020 10:54 PM", formated:
'Tuesday, April 7, 2020 <br /><svg class=\'fa d-icon d-icon-clock svg-icon svg-string\' xmlns="http://www.w3.org/2000/svg"><use xlink:href="#clock" /></svg> 10:54 PM',
timezone: "Sydney", timezone: "Sydney",
}, },
], ],
@ -539,11 +576,13 @@ test("previews", function (assert) {
previews: [ previews: [
{ {
current: true, current: true,
formated: "May 13, 2020 11:00 AM", formated:
'Wednesday, May 13, 2020 <br /><svg class=\'fa d-icon d-icon-clock svg-icon svg-string\' xmlns="http://www.w3.org/2000/svg"><use xlink:href="#clock" /></svg> 11:00 AM',
timezone: "Los Angeles", timezone: "Los Angeles",
}, },
{ {
formated: "May 13, 2020 6:00 PM", formated:
'Wednesday, May 13, 2020 <br /><svg class=\'fa d-icon d-icon-clock svg-icon svg-string\' xmlns="http://www.w3.org/2000/svg"><use xlink:href="#clock" /></svg> 6:00 PM',
timezone: "UTC", timezone: "UTC",
}, },
], ],