131 lines
5.0 KiB
JavaScript
131 lines
5.0 KiB
JavaScript
|
/**
|
||
|
* <js-timestamp>
|
||
|
* -----------------------------------------------------------------------------
|
||
|
* A human-readable, self-updating "timeago" timestamp, with some special rules:
|
||
|
*
|
||
|
* • Within 24 hours, displays in "timeago" format.
|
||
|
* • Within a month, displays month, day, and time of day.
|
||
|
* • Within a year, displays just the month and day.
|
||
|
* • Older/newer than that, displays the month and day with the full year.
|
||
|
*
|
||
|
* @type {Component}
|
||
|
* -----------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
parasails.registerComponent('jsTimestamp', {
|
||
|
|
||
|
// ╔═╗╦═╗╔═╗╔═╗╔═╗
|
||
|
// ╠═╝╠╦╝║ ║╠═╝╚═╗
|
||
|
// ╩ ╩╚═╚═╝╩ ╚═╝
|
||
|
props: [
|
||
|
'at',// « The JS timestamp to format
|
||
|
'short',// « Whether to shorten the formatted date by not including the time of day (may only be used with timeago, and even then only applicable in certain situations)
|
||
|
'format',// « one of: 'calendar', 'timeago' (defaults to 'timeago'. Otherwise, the "calendar" format displays data as US-style calendar dates with a four-character year, separated by dashes. In other words: "MM-DD-YYYY")
|
||
|
],
|
||
|
|
||
|
// ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗
|
||
|
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
|
||
|
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
|
||
|
data: function (){
|
||
|
return {
|
||
|
formatType: undefined,
|
||
|
formattedTimestamp: '',
|
||
|
interval: undefined
|
||
|
};
|
||
|
},
|
||
|
|
||
|
// ╦ ╦╔╦╗╔╦╗╦
|
||
|
// ╠═╣ ║ ║║║║
|
||
|
// ╩ ╩ ╩ ╩ ╩╩═╝
|
||
|
template: `
|
||
|
<span>{{formattedTimestamp}}</span>
|
||
|
`,
|
||
|
|
||
|
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
|
||
|
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
|
||
|
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
|
||
|
beforeMount: function() {
|
||
|
if (this.at === undefined) {
|
||
|
throw new Error('Incomplete usage of <js-timestamp>: Please specify `at` as a JS timestamp (i.e. epoch ms, a number). For example: `<js-timestamp :at="something.createdAt">`');
|
||
|
}
|
||
|
if(this.format === undefined) {
|
||
|
this.formatType = 'timeago';
|
||
|
} else {
|
||
|
if(!_.contains(['calendar', 'timeago'], this.format)) { throw new Error('Unsupported `format` ('+this.format+') passed in to the JS timestamp component! Must be either \'calendar\' or \'timeago\'.'); }
|
||
|
this.formatType = this.format;
|
||
|
}
|
||
|
|
||
|
// If timeago timestamp, update the timestamp every minute.
|
||
|
if(this.formatType === 'timeago') {
|
||
|
this._formatTimeago();
|
||
|
this.interval = setInterval(async()=>{
|
||
|
try {
|
||
|
this._formatTimeago();
|
||
|
await this.forceRender();
|
||
|
} catch (err) {
|
||
|
err.raw = err;
|
||
|
throw new Error('Encountered unexpected error while attempting to automatically re-render <js-timestamp> in the background, as the seconds tick by. '+err.message);
|
||
|
}
|
||
|
},60*1000);//œ
|
||
|
}
|
||
|
|
||
|
// If calendar timestamp, just set it the once.
|
||
|
// (Also don't allow usage with `short`.)
|
||
|
if(this.formatType === 'calendar') {
|
||
|
this.formattedTimestamp = moment(this.at).format('MM-DD-YYYY');
|
||
|
if (this.short) {
|
||
|
throw new Error('Invalid usage of <js-timestamp>: Cannot use `short="true"` at the same time as `format="calendar"`.');
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
beforeDestroy: function() {
|
||
|
if(this.formatType === 'timeago') {
|
||
|
clearInterval(this.interval);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
watch: {
|
||
|
at: function() {
|
||
|
// Render to account for after-mount programmatic changes to `at`.
|
||
|
if(this.formatType === 'timeago') {
|
||
|
this._formatTimeago();
|
||
|
} else if(this.formatType === 'calendar') {
|
||
|
this.formattedTimestamp = moment(this.at).format('MM-DD-YYYY');
|
||
|
} else {
|
||
|
throw new Error();
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
|
||
|
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
|
||
|
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
|
||
|
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
|
||
|
methods: {
|
||
|
|
||
|
_formatTimeago: function() {
|
||
|
var now = new Date().getTime();
|
||
|
var timeDifference = Math.abs(now - this.at);
|
||
|
|
||
|
// If the timestamp is less than a day old, format as time ago.
|
||
|
if(timeDifference < 1000*60*60*24) {
|
||
|
this.formattedTimestamp = moment(this.at).fromNow();
|
||
|
} else {
|
||
|
// If the timestamp is less than a month-ish old, we'll include the
|
||
|
// time of day in the formatted timestamp.
|
||
|
let includeTime = !this.short && timeDifference < 1000*60*60*24*31;
|
||
|
|
||
|
// If the timestamp is from a different year, we'll include the year
|
||
|
// in the formatted timestamp.
|
||
|
let includeYear = moment(now).format('YYYY') !== moment(this.at).format('YYYY');
|
||
|
|
||
|
this.formattedTimestamp = moment(this.at).format('MMMM DD'+(includeYear ? ', YYYY' : '')+(includeTime ? ' [at] h:mma' : ''));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
});
|