Wijmo Slack Status Calendar

This is a Wijmo version of the Slack status page. It shows several Calendar controls and use the formatItem event to add icons to days where significant events took place. Plain CSS is used to cusotmize the appearnace of the Calendar.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wijmo from '@grapecity/wijmo'; import * as input from '@grapecity/wijmo.input'; // import { getEvents } from './data'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // create the events to show on our calendar let events = getEvents(); // // create the calendar let calendar = document.getElementById('calendar'), tooltip = new wijmo.Tooltip(); // for (let i = 0, start = new Date(); i < 12; i++) { let month = createMonthControl(wijmo.DateTime.addMonths(start, -i)); calendar.appendChild(month); } // // create a month function createMonthControl(date) { // create the calendar let month = wijmo.createElement('<div class="month"></div>'), cal = new input.Calendar(month, { showHeader: false, selectionMode: 'None', value: date, formatItem: formatDayCell }); cal.refresh(); // // add a custom header element let fmt = wijmo.format('<div class="month-header">' + '<div class="month-title">{header}</div>' + '<div class="month-status">{uptime}% uptime</div>' + '</div>', { header: wijmo.Globalize.format(date, 'MMMM yyyy'), uptime: getUptime() }); let newHeader = wijmo.createElement(fmt); let hdr = cal.hostElement.querySelector('.wj-calendar-header'); hdr.parentElement.insertBefore(newHeader, hdr); // show only first letter of week day let cells = cal.hostElement.querySelectorAll('table tr.wj-header td'); for (let i = 0; i < 7; i++) { cells[i].textContent = cells[i].textContent.substr(0, 1); } // return month; } // // format the calendar cells to show events function formatDayCell(sender, e) { let event = getEvent(e.data), html = `<div>${e.data.getDate()}</div>`; // html += event ? `<img src="https://status.slack.com/img/v2/Table${event.type}.png"/>` : '<img/>'; // // format cell content e.item.innerHTML = html; // // add tooltip to cell let tip = wijmo.format('<div class="event-tip event-type-{eventType}">' + '<div>{date:MMM d, yyyy}</div>' + '<div class="event">{eventMessage}</div>' + '</div>', { date: e.data, eventMessage: event ? event.msg : 'No incidents, outages, or maintenance.', eventType: event ? event.type.toLocaleLowerCase() : 'none' }); // tooltip.setTooltip(e.item, tip); } // function getUptime() { let tm = [100, 99.75, 99.998, 99.98, 99.996, 99.93]; return tm[Math.floor(Math.random() * tm.length)]; } // function getEvent(date) { for (let i = 0; i < events.length; i++) { if (wijmo.DateTime.sameDate(events[i].date, date)) { return events[i]; } } // return null; } } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Calendar Styling</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <div class="legend"> <div><img src="https://status.slack.com/img/v2/TableMaintenance.png"> Maintenance</div> <div><img src="https://status.slack.com/img/v2/TableIncident.png"> Incident</div> <div><img src="https://status.slack.com/img/v2/TableNotice.png"> Notice</div> <div><img src="https://status.slack.com/img/v2/TableOutage.png"> Outage</div> </div> <div id="calendar" class="calendar"> </div> </div> </body> </html> import { DateTime } from '@grapecity/wijmo'; // // generate some events between now and a year ago export function getEvents() { let arr = [], types = ['Maintenance', 'Incident', 'Notice', 'Outage'], messages = ['Connectivity Issues', 'ISP Reported Problem', 'Message Server Overflow', 'Security Alert', 'Email Failure', 'Power Instability', 'Power Outage']; // for (let i = 0; i < 120; i++) { let dt = DateTime.addDays(new Date(), -Math.round(Math.random() * 365)); arr.push({ id: i, date: dt, type: types[Math.floor(Math.random() * types.length)], msg: messages[Math.floor(Math.random() * messages.length)] }); } // return arr; } @media (min-width: 1200px) { .container-fluid { width: 1250px; } } body { background-color: #f5f5f5; font-size: 16px; font-family: -apple-system-font, 'Segoe UI', 'Roboto', sans-serif; padding: 0 0 10px; } .legend { display: flex; justify-content: flex-end; } .legend div { margin-left: 24px; } .calendar { display: flex; flex-wrap: wrap; justify-content: center; margin-top: 32px; padding-bottom: 90px; } .calendar .wj-calendar { width: 390px; padding: 12px 12px 36px; background-color: white; margin: 8px; } .calendar .wj-calendar .wj-content { border: none; } .calendar .wj-calendar .wj-day-today { font-weight: normal; } .calendar .wj-calendar .wj-state-selected { background: inherit; color: inherit; } .calendar .wj-calendar tr:not(.wj-header) td:hover { background: #eee; } .calendar .wj-calendar .month-header { display: flex; justify-content: space-between; align-items: baseline; font-weight: bold; margin-bottom: 20px; } .calendar .wj-calendar .month-header .month-title{ font-size: 150%; } .calendar .wj-calendar img { height: 12px; margin-top: -12px; } .calendar .wj-calendar-month .wj-header { font-size: 100%; background-color: inherit; font-weight: normal; } .calendar .wj-calendar .wj-day-othermonth { visibility: hidden; } .wj-tooltip { background-color: #fff; padding: 0; border: 1px solid rgba(0,0,0,0.1); border-radius: 0; box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); box-sizing: border-box; width: 450px; } .wj-tooltip .event { margin-top: 12px; font-weight: bold; } .wj-tooltip .event-tip { border-top: 4px solid green; padding: 24px; } .wj-tooltip .event-tip.event-type-maintenance { border-top-color: #3f46ad; } .wj-tooltip .event-tip.event-type-incident { border-top-color: #eeb522; } .wj-tooltip .event-tip.event-type-notice { border-top-color: #ff944b; } .wj-tooltip .event-tip.event-type-outage { border-top-color: #eb4d5c; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wijmo from '@grapecity/wijmo'; import * as input from '@grapecity/wijmo.input'; // import { Component, Inject, enableProdMode, NgModule, OnInit } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { WjInputModule } from '@grapecity/wijmo.angular2.input'; import { DataService, TCalendarEvent } from './app.data'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent implements OnInit { private _events: TCalendarEvent[]; private _tooltip: wijmo.Tooltip; // constructor(@Inject(DataService) private dataService: DataService) { // create the events to show on our calendar this._events = dataService.getEvents(); this._tooltip = new wijmo.Tooltip(); } // ngOnInit() { // create the calendar let calendar = document.querySelector('#calendar'); // for (let i = 0, start = new Date(); i < 12; i++) { let month = this._createMonthControl(wijmo.DateTime.addMonths(start, -i)); calendar.appendChild(month); } } // // create a month private _createMonthControl(date: Date) { // create the calendar let month = wijmo.createElement('<div class="month"></div>'), cal = new input.Calendar(month, { showHeader: false, selectionMode: 'None', value: date, formatItem: this._formatDayCell.bind(this) }); cal.refresh(); // // add a custom header element let fmt = wijmo.format('<div class="month-header">' + '<div class="month-title">{header}</div>' + '<div class="month-status">{uptime}% uptime</div>' + '</div>', { header: wijmo.Globalize.format(date, 'MMMM yyyy'), uptime: this._getUptime() }); let newHeader = wijmo.createElement(fmt); let hdr = cal.hostElement.querySelector('.wj-calendar-header'); hdr.parentElement.insertBefore(newHeader, hdr); // show only first letter of week day let cells = cal.hostElement.querySelectorAll('table tr.wj-header td'); for (let i = 0; i < 7; i++) { cells[i].textContent = cells[i].textContent.substr(0, 1); } // return month; } // // format the calendar cells to show events private _formatDayCell(sender: input.Calendar, e: input.FormatItemEventArgs) { let event = this._getEvent(e.data), html = `<div>${e.data.getDate()}</div>`; // html += event ? `<img src="https://status.slack.com/img/v2/Table${event.type}.png"/>` : '<img/>' // // format cell content e.item.innerHTML = html; // // add tooltip to cell let tip = wijmo.format('<div class="event-tip event-type-{eventType}">' + '<div>{date:MMM d, yyyy}</div>' + '<div class="event">{eventMessage}</div>' + '</div>', { date: e.data, eventMessage: event ? event.msg : 'No incidents, outages, or maintenance.', eventType: event ? event.type.toLocaleLowerCase() : 'none' }); // this._tooltip.setTooltip(e.item, tip) } // private _getUptime() { let tm = [100, 99.75, 99.998, 99.98, 99.996, 99.93]; return tm[Math.floor(Math.random() * tm.length)]; } // private _getEvent(date: Date): TCalendarEvent { for (let i = 0; i < this._events.length; i++) { if (wijmo.DateTime.sameDate(this._events[i].date, date)) { return this._events[i]; } } // return null; } } // @NgModule({ imports: [WjInputModule, BrowserModule], declarations: [AppComponent], providers: [DataService], bootstrap: [AppComponent] }) export class AppModule { } // enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Calendar Styling</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html> <div class="container-fluid"> <div class="legend"> <div><img src="https://status.slack.com/img/v2/TableMaintenance.png"> Maintenance</div> <div><img src="https://status.slack.com/img/v2/TableIncident.png"> Incident</div> <div><img src="https://status.slack.com/img/v2/TableNotice.png"> Notice</div> <div><img src="https://status.slack.com/img/v2/TableOutage.png"> Outage</div> </div> <div id="calendar" class="calendar"> </div> </div> import { DateTime } from '@grapecity/wijmo'; import { Injectable } from '@angular/core'; // export type TCalendarEvent = { id: number; date: Date; type: string; msg: string; } // @Injectable() export class DataService { getEvents() { let arr: TCalendarEvent[] = [], types = ['Maintenance', 'Incident', 'Notice', 'Outage'], messages = ['Connectivity Issues', 'ISP Reported Problem', 'Message Server Overflow', 'Security Alert', 'Email Failure', 'Power Instability', 'Power Outage']; // for (let i = 0; i < 120; i++) { let dt = DateTime.addDays(new Date(), -Math.round(Math.random() * 365)); arr.push({ id: i, date: dt, type: types[Math.floor(Math.random() * types.length)], msg: messages[Math.floor(Math.random() * messages.length)] }); } // return arr; } } @media (min-width: 1200px) { .container-fluid { width: 1250px; } } body { background-color: #f5f5f5; font-size: 16px; font-family: -apple-system-font, 'Segoe UI', 'Roboto', sans-serif; padding: 0 0 10px; } .legend { display: flex; justify-content: flex-end; } .legend div { margin-left: 24px; } .calendar { display: flex; flex-wrap: wrap; justify-content: center; margin-top: 32px; padding-bottom: 90px; } .calendar .wj-calendar { width: 390px; padding: 12px 12px 36px; background-color: white; margin: 8px; } .calendar .wj-calendar .wj-content { border: none; } .calendar .wj-calendar .wj-day-today { font-weight: normal; } .calendar .wj-calendar .wj-state-selected { background: inherit; color: inherit; } .calendar .wj-calendar tr:not(.wj-header) td:hover { background: #eee; } .calendar .wj-calendar .month-header { display: flex; justify-content: space-between; align-items: baseline; font-weight: bold; margin-bottom: 20px; } .calendar .wj-calendar .month-header .month-title{ font-size: 150%; } .calendar .wj-calendar img { height: 12px; margin-top: -12px; } .calendar .wj-calendar-month .wj-header { font-size: 100%; background-color: inherit; font-weight: normal; } .calendar .wj-calendar .wj-day-othermonth { visibility: hidden; } .wj-tooltip { background-color: #fff; padding: 0; border: 1px solid rgba(0,0,0,0.1); border-radius: 0; box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); box-sizing: border-box; width: 450px; } .wj-tooltip .event { margin-top: 12px; font-weight: bold; } .wj-tooltip .event-tip { border-top: 4px solid green; padding: 24px; } .wj-tooltip .event-tip.event-type-maintenance { border-top-color: #3f46ad; } .wj-tooltip .event-tip.event-type-incident { border-top-color: #eeb522; } .wj-tooltip .event-tip.event-type-notice { border-top-color: #ff944b; } .wj-tooltip .event-tip.event-type-outage { border-top-color: #eb4d5c; } <template> <div class="container-fluid"> <div class="legend"> <div><img src="https://status.slack.com/img/v2/TableMaintenance.png"> Maintenance</div> <div><img src="https://status.slack.com/img/v2/TableIncident.png"> Incident</div> <div><img src="https://status.slack.com/img/v2/TableNotice.png"> Notice</div> <div><img src="https://status.slack.com/img/v2/TableOutage.png"> Outage</div> </div> <div id="calendar" class="calendar"> </div> </div> </template> <script> import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import Vue from 'vue'; import '@grapecity/wijmo.vue2.core'; import '@grapecity/wijmo.vue2.input'; import * as input from '@grapecity/wijmo.input'; import { getEvents } from './data' let App = Vue.extend({ name: 'app', data: function () { return { _events: null, _tooltip: null } }, methods: { _createMonthControl: function(date) { // create the calendar let month = wijmo.createElement('<div class="month"></div>'), cal = new input.Calendar(month, { showHeader: false, selectionMode: 'None', value: date, formatItem: this._formatDayCell.bind(this) }); cal.refresh(); // // add a custom header element let fmt = wijmo.format('<div class="month-header">' + '<div class="month-title">{header}</div>' + '<div class="month-status">{uptime}% uptime</div>' + '</div>', { header: wijmo.Globalize.format(date, 'MMMM yyyy'), uptime: this._getUptime() }); let newHeader = wijmo.createElement(fmt); let hdr = cal.hostElement.querySelector('.wj-calendar-header'); hdr.parentElement.insertBefore(newHeader, hdr); // show only first letter of week day let cells = cal.hostElement.querySelectorAll('table tr.wj-header td'); for (let i = 0; i < 7; i++) { cells[i].textContent = cells[i].textContent.substr(0, 1); } return month; }, // format the calendar cells to show events _formatDayCell: function(sender, e) { let event = this._getEvent(e.data), html = `<div>${e.data.getDate()}</div>`; html += event ? `<img src="https://status.slack.com/img/v2/Table${event.type}.png"/>` : '<img/>' // format cell content e.item.innerHTML = html; // add tooltip to cell let tip = wijmo.format('<div class="event-tip event-type-{eventType}">' + '<div>{date:MMM d, yyyy}</div>' + '<div class="event">{eventMessage}</div>' + '</div>', { date: e.data, eventMessage: event ? event.msg : 'No incidents, outages, or maintenance.', eventType: event ? event.type.toLocaleLowerCase() : 'none' }); this._tooltip.setTooltip(e.item, tip) }, _getUptime: function() { let tm = [100, 99.75, 99.998, 99.98, 99.996, 99.93]; return tm[Math.floor(Math.random() * tm.length)]; }, // _getEvent: function(date){ for (let i = 0; i < this._events.length; i++) { if (wijmo.DateTime.sameDate(this._events[i].date, date)) { return this._events[i]; } } // return null; } }, mounted: function(){ this._tooltip = new wijmo.Tooltip(); this._events = getEvents(); // create the calendar let calendar = document.querySelector('#calendar'); // for (let i = 0, start = new Date(); i < 12; i++) { let month = this._createMonthControl(wijmo.DateTime.addMonths(start, -i)); calendar.appendChild(month); } } }) let vm = new Vue({ render: h => h(App) }).$mount('#app'); </script> <style> @media (min-width: 1200px) { .container-fluid { width: 1250px; } } body.body { background-color: #f5f5f5; font-size: 16px; font-family: -apple-system-font, 'Segoe UI', 'Roboto', sans-serif; padding: 0 0 10px; } .legend { display: flex; justify-content: flex-end; } .legend div { margin-left: 24px; } .calendar { display: flex; flex-wrap: wrap; justify-content: center; margin-top: 32px; padding-bottom: 90px; } .calendar .wj-calendar { width: 390px; padding: 12px 12px 36px; background-color: white; margin: 8px; } .calendar .wj-calendar .wj-content { border: none; } .calendar .wj-calendar .wj-day-today { font-weight: normal; } .calendar .wj-calendar .wj-state-selected { background: inherit; color: inherit; } .calendar .wj-calendar tr:not(.wj-header) td:hover { background: #eee; } .calendar .wj-calendar .month-header { display: flex; justify-content: space-between; align-items: baseline; font-weight: bold; margin-bottom: 20px; } .calendar .wj-calendar .month-header .month-title{ font-size: 150%; } .calendar .wj-calendar img { height: 12px; margin-top: -12px; } .calendar .wj-calendar-month .wj-header { font-size: 100%; background-color: inherit; font-weight: normal; } .calendar .wj-calendar .wj-day-othermonth { visibility: hidden; } .container-fluid .wj-tooltip { background-color: #fff; padding: 0; border: 1px solid rgba(0,0,0,0.1); border-radius: 0; box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); box-sizing: border-box; width: 450px; } .container-fluid .wj-tooltip .event { margin-top: 12px; font-weight: bold; } .container-fluid .wj-tooltip .event-tip { border-top: 4px solid green; padding: 24px; } .container-fluid .wj-tooltip .event-tip.event-type-maintenance { border-top-color: #3f46ad; } .container-fluid .wj-tooltip .event-tip.event-type-incident { border-top-color: #eeb522; } .container-fluid .wj-tooltip .event-tip.event-type-notice { border-top-color: #ff944b; } .container-fluid .wj-tooltip .event-tip.event-type-outage { border-top-color: #eb4d5c; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Calendar Styling</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app.vue'); </script> </head> <body class="body"> <div id="app"></div> </body> </html> import { DateTime } from '@grapecity/wijmo'; // // generate some events between now and a year ago export function getEvents() { let arr = [], types = ['Maintenance', 'Incident', 'Notice', 'Outage'], messages = ['Connectivity Issues', 'ISP Reported Problem', 'Message Server Overflow', 'Security Alert', 'Email Failure', 'Power Instability', 'Power Outage']; // for (let i = 0; i < 120; i++) { let dt = DateTime.addDays(new Date(), -Math.round(Math.random() * 365)); arr.push({ id: i, date: dt, type: types[Math.floor(Math.random() * types.length)], msg: messages[Math.floor(Math.random() * messages.length)] }); } // return arr; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './app.css'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import * as wijmo from '@grapecity/wijmo'; import * as wjInput from '@grapecity/wijmo.input'; import { getEvents } from './data'; class App extends React.Component { constructor(props) { super(props); this._createMonthControl = (date) => { // create the calendar let month = wijmo.createElement('<div class="month"></div>'), cal = new wjInput.Calendar(month, { showHeader: false, selectionMode: 'None', value: date, formatItem: this._formatDayCell.bind(this) }); cal.refresh(); // // add a custom header element let fmt = wijmo.format('<div class="month-header">' + '<div class="month-title">{header}</div>' + '<div class="month-status">{uptime}% uptime</div>' + '</div>', { header: wijmo.Globalize.format(date, 'MMMM yyyy'), uptime: this._getUptime() }); let newHeader = wijmo.createElement(fmt); let hdr = cal.hostElement.querySelector('.wj-calendar-header'); hdr.parentElement.insertBefore(newHeader, hdr); // show only first letter of week day let cells = cal.hostElement.querySelectorAll('table tr.wj-header td'); for (let i = 0; i < 7; i++) { cells[i].textContent = cells[i].textContent.substr(0, 1); } return month; }; // format the calendar cells to show events this._formatDayCell = (sender, e) => { let event = this._getEvent(e.data), html = `<div>${e.data.getDate()}</div>`; html += event ? `<img src="https://status.slack.com/img/v2/Table${event.type}.png"/>` : '<img/>'; // format cell content e.item.innerHTML = html; // add tooltip to cell let tip = wijmo.format('<div class="event-tip event-type-{eventType}">' + '<div>{date:MMM d, yyyy}</div>' + '<div class="event">{eventMessage}</div>' + '</div>', { date: e.data, eventMessage: event ? event.msg : 'No incidents, outages, or maintenance.', eventType: event ? event.type.toLocaleLowerCase() : 'none' }); this.state.tooltip.setTooltip(e.item, tip); }; this._getUptime = () => { let tm = [100, 99.75, 99.998, 99.98, 99.996, 99.93]; return tm[Math.floor(Math.random() * tm.length)]; }; this._getEvent = (date) => { for (let i = 0; i < this.state.events.length; i++) { if (wijmo.DateTime.sameDate(this.state.events[i].date, date)) { return this.state.events[i]; } } return null; }; this.state = { events: getEvents(), tooltip: new wijmo.Tooltip() }; } render() { return <div className="container-fluid"> <div className="legend"> <div><img src="https://status.slack.com/img/v2/TableMaintenance.png"/>> Maintenance</div> <div><img src="https://status.slack.com/img/v2/TableIncident.png"/>> Incident</div> <div><img src="https://status.slack.com/img/v2/TableNotice.png"/>> Notice</div> <div><img src="https://status.slack.com/img/v2/TableOutage.png"/>> Outage</div> </div> <div id="calendar" className="calendar"></div> </div>; } componentDidMount() { // create the calendar let calendar = document.querySelector('#calendar'); // for (let i = 0, start = new Date(); i < 12; i++) { let month = this._createMonthControl(wijmo.DateTime.addMonths(start, -i)); calendar.appendChild(month); } } } ReactDOM.render(<App />, document.getElementById('app')); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AutoComplete</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html> @media (min-width: 1200px) { .container-fluid { width: 1250px; } } body { background-color: #f5f5f5; font-size: 16px; font-family: -apple-system-font, 'Segoe UI', 'Roboto', sans-serif; padding: 0 0 10px; } .legend { display: flex; justify-content: flex-end; } .legend div { margin-left: 24px; } .calendar { display: flex; flex-wrap: wrap; justify-content: center; margin-top: 32px; padding-bottom: 90px; } .calendar .wj-calendar { width: 390px; padding: 12px 12px 36px; background-color: white; margin: 8px; } .calendar .wj-calendar .wj-content { border: none; } .calendar .wj-calendar .wj-day-today { font-weight: normal; } .calendar .wj-calendar .wj-state-selected { background: inherit; color: inherit; } .calendar .wj-calendar tr:not(.wj-header) td:hover { background: #eee; } .calendar .wj-calendar .month-header { display: flex; justify-content: space-between; align-items: baseline; font-weight: bold; margin-bottom: 20px; } .calendar .wj-calendar .month-header .month-title{ font-size: 150%; } .calendar .wj-calendar img { height: 12px; margin-top: -12px; } .calendar .wj-calendar-month .wj-header { font-size: 100%; background-color: inherit; font-weight: normal; } .calendar .wj-calendar .wj-day-othermonth { visibility: hidden; } .wj-tooltip { background-color: #fff; padding: 0; border: 1px solid rgba(0,0,0,0.1); border-radius: 0; box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); box-sizing: border-box; width: 450px; } .wj-tooltip .event { margin-top: 12px; font-weight: bold; } .wj-tooltip .event-tip { border-top: 4px solid green; padding: 24px; } .wj-tooltip .event-tip.event-type-maintenance { border-top-color: #3f46ad; } .wj-tooltip .event-tip.event-type-incident { border-top-color: #eeb522; } .wj-tooltip .event-tip.event-type-notice { border-top-color: #ff944b; } .wj-tooltip .event-tip.event-type-outage { border-top-color: #eb4d5c; } import { DateTime } from '@grapecity/wijmo'; export function getEvents() { let arr = [], types = ['Maintenance', 'Incident', 'Notice', 'Outage'], messages = ['Connectivity Issues', 'ISP Reported Problem', 'Message Server Overflow', 'Security Alert', 'Email Failure', 'Power Instability', 'Power Outage']; for (let i = 0; i < 120; i++) { let dt = DateTime.addDays(new Date(), -Math.round(Math.random() * 365)); arr.push({ id: i, date: dt, type: types[Math.floor(Math.random() * types.length)], msg: messages[Math.floor(Math.random() * messages.length)] }); } return arr; }