This allows consumers to pass in, and receive, timestamps for a different timezone. Previously, attempting this would lead to very strange behavior which would become worse the further the input timestamp's timezone was from the browser's timezone. The default behavior is unchanged - the browser's timezone will be assumed.
188 lines
4.3 KiB
JavaScript
188 lines
4.3 KiB
JavaScript
import { action, computed } from "@ember/object";
|
|
import Component from "@ember/component";
|
|
import { isPresent } from "@ember/utils";
|
|
import { htmlSafe } from "@ember/template";
|
|
|
|
function convertMinutes(num) {
|
|
return { hours: Math.floor(num / 60), minutes: num % 60 };
|
|
}
|
|
|
|
function convertMinutesToString(n) {
|
|
const hoursAndMinutes = convertMinutes(n);
|
|
return `${hoursAndMinutes.hours
|
|
.toString()
|
|
.padStart(2, "0")}:${hoursAndMinutes.minutes.toString().padStart(2, "0")}`;
|
|
}
|
|
|
|
function convertMinutesToDurationString(n) {
|
|
const hoursAndMinutes = convertMinutes(n);
|
|
|
|
let output = "";
|
|
|
|
if (hoursAndMinutes.hours) {
|
|
output = `${hoursAndMinutes.hours}h`;
|
|
|
|
if (hoursAndMinutes.minutes > 0) {
|
|
output = `${output} ${hoursAndMinutes.minutes} min`;
|
|
}
|
|
} else {
|
|
output = `${hoursAndMinutes.minutes} min`;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
export default Component.extend({
|
|
classNames: ["d-time-input"],
|
|
|
|
hours: null,
|
|
|
|
minutes: null,
|
|
|
|
relativeDate: null,
|
|
|
|
didReceiveAttrs() {
|
|
this._super(...arguments);
|
|
|
|
if (isPresent(this.date)) {
|
|
this.setProperties({
|
|
hours: this.date.hours(),
|
|
minutes: this.date.minutes(),
|
|
});
|
|
}
|
|
|
|
if (
|
|
!isPresent(this.date) &&
|
|
!isPresent(this.attrs.hours) &&
|
|
!isPresent(this.attrs.minutes)
|
|
) {
|
|
this.setProperties({
|
|
hours: null,
|
|
minutes: null,
|
|
});
|
|
}
|
|
},
|
|
|
|
minimumTime: computed("relativeDate", "date", function () {
|
|
if (this.relativeDate) {
|
|
if (this.date) {
|
|
if (!this.date.isSame(this.relativeDate, "day")) {
|
|
return 0;
|
|
} else {
|
|
return this.relativeDate.hours() * 60 + this.relativeDate.minutes();
|
|
}
|
|
} else {
|
|
return this.relativeDate.hours() * 60 + this.relativeDate.minutes();
|
|
}
|
|
}
|
|
}),
|
|
|
|
timeOptions: computed("minimumTime", "hours", "minutes", function () {
|
|
let options = [];
|
|
|
|
const start = this.minimumTime
|
|
? this.minimumTime > this.time
|
|
? this.time
|
|
: this.minimumTime
|
|
: 0;
|
|
|
|
// theres 1440 minutes in a day
|
|
// and 1440 / 15 = 96
|
|
let i = 0;
|
|
while (i < 96) {
|
|
// while diff with minimumTime is less than one hour
|
|
// use 15 minutes steps and then 30 minutes
|
|
const minutes = this.minimumTime ? (i <= 4 ? 15 : 30) : 15;
|
|
const option = start + i * minutes;
|
|
|
|
// when start is higher than 0 we will reach 1440 minutes
|
|
// before the 96 iterations
|
|
if (option > 1440) {
|
|
break;
|
|
}
|
|
|
|
options.push(option);
|
|
|
|
i++;
|
|
}
|
|
|
|
if (this.time && !options.includes(this.time)) {
|
|
options = [this.time].concat(options);
|
|
}
|
|
|
|
options = options.sort((a, b) => a - b);
|
|
|
|
return options.map((option) => {
|
|
let name = convertMinutesToString(option);
|
|
let label;
|
|
|
|
if (this.date && this.relativeDate) {
|
|
const diff = this.date
|
|
.clone()
|
|
.startOf("day")
|
|
.add(option, "minutes")
|
|
.diff(this.relativeDate, "minutes");
|
|
|
|
if (diff < 1440) {
|
|
label = htmlSafe(
|
|
`${name} <small>(${convertMinutesToDurationString(diff)})</small>`
|
|
);
|
|
}
|
|
}
|
|
|
|
return {
|
|
id: option,
|
|
name,
|
|
label,
|
|
title: name,
|
|
};
|
|
});
|
|
}),
|
|
|
|
time: computed("minimumTime", "hours", "minutes", function () {
|
|
if (isPresent(this.hours) && isPresent(this.minutes)) {
|
|
return parseInt(this.hours, 10) * 60 + parseInt(this.minutes, 10);
|
|
}
|
|
}),
|
|
|
|
@action
|
|
onFocusIn(value, event) {
|
|
if (value && event.target) {
|
|
event.target.select();
|
|
}
|
|
},
|
|
|
|
@action
|
|
onChangeTime(time) {
|
|
if (isPresent(time) && this.onChange) {
|
|
if (typeof time === "string" && time.length) {
|
|
let [hours, minutes] = time.split(":");
|
|
if (hours && minutes) {
|
|
if (hours < 0) {
|
|
hours = 0;
|
|
}
|
|
if (hours > 23) {
|
|
hours = 23;
|
|
}
|
|
if (minutes < 0) {
|
|
minutes = 0;
|
|
}
|
|
if (minutes > 59) {
|
|
minutes = 59;
|
|
}
|
|
|
|
this.onChange({
|
|
hours: parseInt(hours, 10),
|
|
minutes: parseInt(minutes, 10),
|
|
});
|
|
}
|
|
} else {
|
|
this.onChange({
|
|
hours: convertMinutes(time).hours,
|
|
minutes: convertMinutes(time).minutes,
|
|
});
|
|
}
|
|
}
|
|
},
|
|
});
|