@lemonadejs/timeline
Version:
LemonadeJS timeline component
216 lines (179 loc) • 7.54 kB
JavaScript
if (!lemonade && typeof (require) === 'function') {
var lemonade = require('lemonadejs');
}
; (function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.Timeline = factory();
}(this, (function () {
let dateSignature = null;
// Dispatcher
const Dispatch = function (type, option) {
if (typeof this[type] === 'function') {
this[type](this, option);
}
}
const TimelineEvents = function () {
this.onload = function () {
if (this.borderColor) {
this.el.style.setProperty('--lm-border-color', this.borderColor);
}
if (this.borderStyle) {
this.el.style.setProperty('--lm-border-style', this.borderStyle);
}
}
return `<div class="lm-timeline-item" data-bullet="{{self.day}}">
<div class="lm-timeline-title">{{self.title}}</div>
<div class="lm-timeline-subtitle">{{self.subtitle}}</div>
<div class="lm-timeline-description">{{self.description}}</div>
</div>`;
}
const Timeline = function () {
let self = this;
let date = new Date();
self.result = [];
if (!Array.isArray(self.data)) {
self.data = [];
}
self.year = date.getFullYear();
self.month = 1 + date.getMonth();
self.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// Error message
if (!self.message) {
self.message = 'No records found';
}
if (!self.order) {
self.order = 'asc';
}
// Make sure to align has a default
if (!['top', 'right', 'bottom', 'left'].includes(self.align)) {
self.align = 'left';
}
const isRemote = function() {
return self.remote && self.url && self.type === 'monthly';
}
const getDate = function(d) {
let date = d && d === dateSignature ? '' : d;
dateSignature = d;
return date;
}
self.next = function () {
if (self.month === 12) {
self.year++;
self.month = 1;
} else {
self.month++;
}
}
self.prev = function () {
if (self.month === 1) {
self.year--;
self.month = 12;
} else {
self.month--;
}
}
self.updateResult = function () {
let result = [];
if (self.type === 'monthly') {
for (let i = 0; i < self.data.length; i++) {
if ((self.data[i].date.getMonth() + 1) === self.month && self.data[i].date.getFullYear() === self.year) {
result.push(self.data[i]);
}
}
} else {
result = self.data;
}
result = result.sort((a, b) => (self.order === 'desc' ? -1 : 1) * (a.date.getTime() - b.date.getTime()));
for (let i = 0; i < result.length; i++) {
result[i].day = getDate(result[i].date.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: '2-digit' }));
}
self.result = result;
// Reset the date signature to avoid interference in the next rendering
dateSignature = null;
// Event
Dispatch.call(self, 'onupdate', self, result);
}
self.fetchRemote = function () {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let res = JSON.parse(xhr.responseText);
let result = [];
if (Array.isArray(res.result)) {
result = res.result;
} else if (Array.isArray(res)) {
result = res
}
for (let i = 0; i < result.length; i++) {
result[i].date = new Date(result[i].date);
result[i].day = getDate(result[i].date.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: '2-digit' }));
}
if (isRemote()) {
self.result = result;
} else {
self.data = result;
}
} else {
console.error('Failed to fetch data. Status code: ' + xhr.status);
}
}
};
let url = self.url;
if (isRemote()) {
url += `?year=${self.year}&month=${self.month}`;
url += `&asc=${self.order === 'asc'}`
}
xhr.open('GET', url, true);
xhr.setRequestHeader('Content-Type', 'text/json')
xhr.send();
}
lemonade.onchange(function(prop) {
if (prop === 'data' || prop === 'month' || prop === 'order') {
if (isRemote()) {
self.fetchRemote();
} else {
self.updateResult();
}
}
})
lemonade.onload(function () {
if (self.url) {
self.fetchRemote();
} else {
self.updateResult();
}
if (typeof (self.width) !== 'undefined') {
self.el.style.width = parseInt(self.width) + 'px';
}
if (typeof (self.height) !== 'undefined') {
self.el.style.height = parseInt(self.height) + 'px';
}
});
return render => render`<div class="lm-timeline" :d="self.data" :d-order="self.order">
<div class="lm-timeline-header" data-visible="{{self.controls}}" data-type="{{self.type}}">
<div class="lm-timeline-label">
<div class="lm-timeline-month">{{self.months[self.month - 1]}}</div>
<div class="lm-timeline-year">{{self.year}}</div>
</div>
<div class="lm-timeline-navigation">
<i class="material-icons" onclick="self.prev">keyboard_arrow_left</i>
<i class="material-icons" onclick="self.next">keyboard_arrow_right</i>
</div>
</div>
<div class="lm-timeline-data" data-message="{{self.message}}" data-mode="{{self.position}}" data-align="{{self.align}}">
<TimelineEvents :loop="self.result" />
</div>
</div>`;
}
lemonade.setComponents({ Timeline: Timeline, TimelineEvents: TimelineEvents });
return function (root, options) {
if (typeof (root) === 'object') {
lemonade.render(Timeline, root, options);
return options;
} else {
return Timeline.call(this, root);
}
}
})));