Casper Bottelet
5 min readJun 23, 2020

--

Building a Horizontal Calendar with VueJS and Vis.js Part 1/3

Hello,

Today I wanna show my progress in building a Horizontal Calendar, what different packages I’ve looked at, and why I ended up going with Vis.js.

If you have any questions along the way feel free to reach out on.

If you are just curious about the final code you can find the full example/gist here:

https://gist.github.com/Bottelet/9eda4b6b3d587635c5ad7c7bc1d8132d

Disclaimer I do not work with Vuejs professionally, which means there will probably be more efficient ways to do some things. I write these posts to improve myself and I am learning as I go.

Prerequisites/Requirments

My research started looking at different packages the options primarily came down to these three:

  1. https://visjs.org/ (Timeline)
  2. https://github.com/ka215/jquery.timeline
  3. https://github.com/neuronetio/gantt-schedule-timeline-calendar

So the third option even though it looked very nice was discarded because of the licensing model they have (AGPL-3.0) which I am no licensing expert, and could not get a totally clear answer what that exactly means about how I am allowed to use it was not worth the “risk” for me, and it is a commercial project I need this timeline for.

The second option also seemed very nice, but first was very dependent on jquery (Which could have been ok) but did not have the same amount of features, one thing I was looking for was the drag n drop. I could probably have implemented it myself but as the first option already had it built-in, and are using vanilla JS, it seemed like the best option for this case.

Let’s start…

The first I had to do was to see if Vis.js would fit all my needs.

my requirements on a helicopter perspective were:

  • Horizontal calendar
  • Multiple users as rows
  • Hours and days scaling
  • Drag n drop calendar entries to new times, or new users. Which should fire an event so I can update the entry “real-time”
  • Expand/resize the time of the calendar entry.
  • Entries at the same time would be showed by either stacking or flowing into each other with another color to make it obvious
  • Show open business hours only
  • Look nice-ish (Not the best designer :))

Lets code…

I’m using Laravel as a Framework, in general, this shouldn’t have to much influence on how you do anything, but there will be a bit backend which uses some Laravel “terms” but can be done with any backend you choose.

First of I had to create a new Migration, Model, Controller, and Route. I won’t be going into much of this as it’s basic Laravel, and this isn’t gonna be too much about Laravel.

app/Http/Controllers/AppointmentsController.phppublic function calendar(){return view(‘appointments.calendar’);}routes/web.phpRoute::get(‘/appointments/calendar’, ‘AppointmentsController@calendar’)->name(‘appointments.calendar’);

Then I started playing around with the calendar, see if I could get it to work with my needs, I started just doing this in the view blade, and did not introduce Vue.js yet.

Let’s start by adding the Vis CSS and JS

<link href=”//unpkg.com/vis-timeline@latest/dist/vis-timeline-graph2d.min.css” rel=”stylesheet” type=”text/css” /><script type=”text/javascript” src=”//unpkg.com/vis-timeline@latest/dist/vis-timeline-graph2d.min.js”></script>

Let’s initialize the calendar on a div with an id of visualization as that’s what the official documentation shows.

<div id=”wrapper”><div id=”visualization”></div></div><script>var container = document.getElementById(‘visualization’);var timeline = new vis.Timeline(container);</script>

To test that Vis.js can satisfy the needs we have let’s start by adding some dummy data of entries and groups which are gonna be users in our case.

Inside our script tag add the following data:

var items = [{id: 1, content: “item 1”, start: ‘2020–04–01 10:00:00’, end: ‘2020–04–01 11:00:00’, group: 1},{id: 2, content: ‘item 2’, start: ‘2020–04–01 09:00:00’, end: ‘2020–04–01 10:00:00’, group: 0},{id: 3, content: ‘item 3’, start: ‘2020–04–03 08:00:00’, end: ‘2020–04–03 10:00:00’, group: 1},];var groups = [{id:0,content: ‘Group 1’},{id:1,content: ‘Group 2’},];var options = {orientation: “top”, //Add the timeline to the topitemsAlwaysDraggable:true, // Dont have to click for moving entries around};var container = document.getElementById(‘visualization’);var timeline = new vis.Timeline(container);timeline.setOptions(options);timeline.setGroups(groups);timeline.setItems(items);

And let’s add a little bit of style to make it look half decent

<style>.vis-time-axis .vis-grid.vis-odd {background: #f5f5f5;}.vis-time-axis .vis-grid.vis-saturday,.vis-time-axis .vis-grid.vis-sunday {background: gray;}.vis-time-axis .vis-text.vis-saturday,.vis-time-axis .vis-text.vis-sunday {color: white;}.vis-item {background-color: #d6e6ff;border-color: #1371fe;color: #0155d3;border-left-width: 3px;border-left-style: solid;font-weight: 500;opacity: .8;font-size: 13px;height: 55px;}</style>

Let’s add some more options to our timeline to make it more interactive and towards what we need. (more options added in the bottom of this code block)

This is also how your script should look by now:

<script>// DOM element where the Timeline will be attachedvar container = document.getElementById(‘visualization’);var items = [{id: 1, content: “Item 1”, start: ‘2020–04–01 10:00:00’, end: ‘2020–04–01 11:00:00’, group: 1},{id: 2, content: ‘item 2’, start: ‘2020–04–01 09:00:00’, end: ‘2020–04–01 10:00:00’, group: 0},{id: 3, content: ‘item 3’, start: ‘2020–04–03 08:00:00’, end: ‘2020–04–03 10:00:00’, group: 1},];var groups = [{id:0,content: ‘Group 1’// Optional: a field ‘className’, ‘style’, ‘order’, [properties]},{id:1,content: ‘Group 2’// Optional: a field ‘className’, ‘style’, ‘order’, [properties]}// more groups…];// Configuration for the Timelinevar options = {orientation: “top”,itemsAlwaysDraggable:true,timeAxis: {scale: ‘hour’, step: 1, },zoomMax: 150000000,//for days 7 instead of 6?minHeight:’500px’,start: ‘2020–04–01’,hiddenDates: { // Show only times between 06:00–18:00start: ‘2020–03–04 18:00:00’,end: ‘2020–03–05 06:00:00’,repeat: ‘daily’},editable: {add: true, // add new items by double tappingupdateTime: true, // drag items horizontallyupdateGroup: true, // drag items from one group to anotherremove: true, // delete an item by tapping the delete button top rightoverrideItems: false // allow these options to override item.editable},onUpdate: function (item, callback) { //Fires event on updateitem.content = prompt(‘Edit items text:’, item.content);if (item.content != null) {callback(item); // send back adjusted item}else {callback(null); // cancel updating the item}},onMove: function (item, callback) { // fire events on moveconsole.log(item, callback)if (item.content != null) {callback(item); // send back adjusted item}else {callback(null); // cancel updating the item}},};// Create a Timelinevar timeline = new vis.Timeline(container);timeline.setOptions(options);timeline.setGroups(groups);timeline.setItems(items);</script>

Now we have a working calendar let’s migrate it to VueJS in step 2

--

--