Styling The Timetable View in JavaScript Scheduler

In this blog post we are going to look at the most common adjustments in the appearance of a timetable that developers want to make based on the questions we have received regarding MindFusion Scheduler library for Java Script.

Here is how our timetable will look at the end:

Styling the Timetable View in JS Scheduler

I. General Looks

The overall appearance of the scheduling library is controlled by themes. MindFusion Scheduler comes with a collection of 9 predefined themes. In order to apply a theme you need to:

  • add a reference to the CSS file in the web page where you want the calendar to appear:
    <link rel="stylesheet" type="text/css" href="themes/gray.css">
  • assign the name of the theme to the theme property of the Calendar class:
    calendar.theme = "gray";
  • Themes can change dramatically the color scheme of the control including the forms that appear for item creation. We recommend that you choose the theme that is most closely related to the colors you want to see and customize it, if necessary.

    Themes in the JavaScript Scheduler

    Each of the themes is in a CSS file and you can search by the color code and replace the occurrences of a given color with another one to fine-tune the color scheme.

    II. Timetable Settings

    Initially, a timetable renders only one day – today. The timetableSettings property, just like the *settings properties for all views supported by the Calendar (—SingleMonth, RespurceView, MonthRange etc.) expose the properties that let you customize the look of the calendar.

    In the case with the timetableView, the number of columns that are rendered depends on the number of dates added to the dates propŠµrty of timetableSettings In our case we want to show the week, so we add 7 dates:

    //get the current date
    var currDay = schedule.DateTime.today();
    calendar.timetableSettings.dates.clear();
    
    for (var i = 1; i < 8; i++) {
    	calendar.timetableSettings.dates.add(currDay.addDays(-1 * currDay.dayOfWeek + i));	
    	}
    }

    Setting the Number Of Dates in a Timetable in the JavaScript Scheduler

    We also want to scroll week by week. By default the timetable scrolls with one day. In order to scroll more you need to set the scrollStep property to a bigger number:

    calendar.timetableSettings.scrollStep = 7;

    The next thing that we need is to change the time scale. We want the scales to be per 20 minutes and we want to cover the time interval from 10 to 16 o’clock. These is regulated with the startTime and endTime fields of timetableSettings These properties take as an argument the number of minutes. So, if you want your day to start at 8 o’clock your start time needs to be 8×60=480 minutes or you need to assign 480 as value to the startTime property.

    // set the start time to 10:00 AM
    calendar.timetableSettings.  = 600;
    // set the end time to 16:00 PM
    calendar.timetableSettings.endTime = 1020;

    What we want to specify is the format of the header. The default format is based on the locale settings of the user that runs the application. In our application the date in the timetable header renders as DD/MM/YYYY. We will use the titleFormat property. We also use cellTime to change the time scales between each two hours. The default value is 30 minutes. We change it to 20 with the cellTime property.

    calendar.timetableSettings.titleFormat = "d MMMM 
     dddd";
    calendar.timetableSettings.cellTime = schedule.TimeSpan.fromMinutes(20);
    calendar.timetableSettings.cellSize = 20;

    Adjusting the Timetable Settings in the JavaScript Scheduler

    We also increase the cell size – this is the height of rows that are defined by each 20-minute interval. The calendar also shows just one header – that with the dates. We want to render the days header, which shows the day of the week. The property for that is showDayHeader

    calendar.timetableSettings.showDayHeader = true;

    III. CSS Styling

    We’ve customized our timetable as much as we could through the properties and fields of the Calendar control. We would like to add some additional styling, which can be done through css. We use the style inspector of the browser to identify the styles that are applied on the elements that interest us. We would like to show the lines that separate 20 minute cells in yellow and the lines that separate hours in red. Let’s start with the hour lines. The css to render them in red is this one:

    .mfp-timetable-view .mfp-content .mfp-column .mfp-cell-wrap:nth-child(3n+1) .mfp-cell { 
    			border-top: 1px solid red; 
    }

    The class that styles cells is called mfo-cell-wrap. This class regulates the styling for all cells, so we need to apply red border only on the cells that interest us, and they are the 1st, 4th, 9th etc. cell. We want the rest of the cells to be yellow. This is done with the CSS “not” keyword:

    .mfp-timetable-view .mfp-content .mfp-column .mfp-cell-wrap:not(:nth-child(3n+1)) .mfp-cell {  
    			border-top: 1px solid yellow; 
    	}

    This colors the rows of the timetable red/yellow but does not color the delimeters between the time scales. They are regulated by another CSS class and are div elements:

    .mfp-timetable-view.gray .mfp-header-timeline .mfp-group-time div:not(:first-child)
    	{
    
    		border-top: solid 1px yellow;
    	}
    		
    	.mfp-timetable-view.gray .mfp-header-timeline .mfp-hour
    	{
    		border-top: solid 1px red;
    	}

    Note that the CSS style names are with the suffix “gray”. This is the name of the theme. In many cases the class that needs to be changed is bound to a certain theme.

    The last thing that we want to add as styling is a background for the weekend days. We use again the nth-child CSS property. This time the “children” are the 6th and 7th element, so we define styles for them:

    .mfp-timetable-view .mfp-content .mfp-column:nth-child(5n + 6), .mfp-column:nth-child(5n + 7) {
    			background-color: rgba(145, 179, 188, 0.4);
    		}

    We note one more thing. When we create an appointment, the text of the subject is not visible because the line height is too small for the item styling. We have two options: either to increase the cell height, which is set with cellSize and is 20 or to style the item, so that the subject is visible. We choose the latter. We will make the resize line-s smaller because that’s what hiding the subject: the big resize lines:

    .mfp-item-vertical-detail .mfp-subject {
    	flex-shrink: 0 !important;
    }
    
    .mfp-item-vertical-detail .mfp-resize-start,
    .mfp-item-vertical-detail .mfp-resize-end {
    	flex-shrink: 1 !important;
    }

    Here is the final result:

    Styling the Timetable Vew in JS Scheduler

    You can download the sample with all libraries ued and the full source code from this link:

    Styling a Timetable in the JavaScript Scheduler

    You can post technical questions, comments and recommendations about MindFusion Scheduling for JavaScript at the library online forum.

    About Scheduling for JavaScript: MindFusion Js Scheduler is the right solution for all applications that need to render interactive timetables, rich event calendars, lists with appointments or resources. Fully responsive, highly customizable and easy to integrate, you can quickly program the JavaScript scheduling library according to your needs. The library supports a variety of export options, styling through themes, 6 calendar views and much more. Find out more at https://mindfusion.eu/javascript-scheduler.html

    Interactive Calendar With Events In JavaScript

    In this blog post we will create a Google-like interactive monthly calendar where users will be able to create, edit and delete appointments in real time. We
    will use the JavaScript Scheduler. Here is a screenshot of the finished application:

    JavaScript Schedule That Mirrors the Google Calendar Features

    JavaScript Schedule That Mirrors the Google Calendar Features

    And you can run the sample online from this link.

    I. Project Setup

    We need a reference to the following file to start development:

    • MindFusion.Scheduling.js
    • light.css

    The JavaScript file provides the scheduling functionality. The CSS file is responsible for the styling of our calendar. We create a subfolder named “themes” and we place the light.css file there.

    We create an HTML file, a blank web page called GoogleSchedule and in the head section we place a reference to the two CSS file:

     

    The reference to the JavaScript file goes at the bottom of the page, right before the closing body tag.

    http://MindFusion.Scheduling.js

     

    We need a element that will represent the calendar, we create one in HTML code and assign it an id:

    We want the calendar to take the whole page, that’s why width and height are 100%.

    II. Create and Customize the Scheduler

    Now we are ready to do the real JavaScript programming of the calendar library. We create an empty JS file called “GoogleSchedule.js” and add a reference to it in the web page, at the bottom:

    http://GoogleSchedule.js

     

    In this JavaScript file we first create a mapping to the MindFusion.Scheduling namespace:

    var p = MindFusion.Scheduling;

     

    Then we create a Calendar instance using the Calendar DOM element from the web page:

    // create a new instance of the calendar 
    var calendar = new p.Calendar(document.getElementById("calendar"));

     

    We set the calendar view to CalendarView .SingleMonth, which means the calendar shows one month at a time. We also set the theme that we referenced in the CSS file:

    calendar.currentView = p.CalendarView.SingleMonth;
    calendar.theme = "light";

     

    Another customization we make – we use the itemSettings.titleFormat property to add a prefix before each event subject. The prefix is the start time of this event. here is how you set it:

    calendar.itemSettings.titleFormat = "%s[hh:mm tt] %h";

     

    Finally, we render the calendar:

    //visualize the calendar
    calendar.render();

     

    III. Custom Form

    By default, the form that renders when the user clicks on a cell or selection of cells in the calendar allows edit of the dates, but not hours. We will render our custom form, derived from BaseForm to change that. Our form will offer the users a list with hours for the selected dates.

    We create a new, empty JavaScript file “TimeForm.js”. There we override the constructor of the BaseForm:

     TimeForm = function (calendar, item, type)
    {
    	p.BaseForm.call(this, calendar, item);
    
    	this._id = "TimeForm";
    	this._type = type;
    	this.headerText = "Appointment";
    	
    }

     

    As you can see from the constructor each BaseForm provides data for the calendar and the item that is created or edited. We will use that information later on. We create a prototype of our form and assign the constructor:

    TimeForm.prototype = Object.create(p.BaseForm.prototype);
    TimeForm.prototype.constructor = TimeForm;

     

    TimeForm will be a simple one – it just renders a text area and two combo boxes with the possible hours for the start and end of the appointment. The combo boxes require a list with the members to be rendered. We create a list as a global variable:

    var hoursList;

     

    Then, in a new method we fill the list with the hours from 12 to 1, divided in intervals of 30 minutes:

    // create an array of objects to fill the hours drop-down
    TimeForm.prototype.getHourLabels = function ()
    {
    	hoursList = [];
    	hoursList.push({ value: 0, text: "12:00am" });
    	hoursList.push({ value: 1, text: "12:30am" });
    	
    	let index = 1;
    	
    	for(var i = 1; i < 12; i++)
    	{
    		hoursList.push({ value: index+1, text: i.toString() + ":00am" });
    	    hoursList.push({ value: index+2, text: i.toString() + ":30am" });
    		
    		index += 2;
    	}
    	
    	//add the first afternnon hours
    	hoursList.push({ value: index + 1, text: "12:00pm" });
    	hoursList.push({ value: index + 2, text: "12:30pm" });
    	
    	index += 2;
    	
    	for(i = 1; i < 12; i++)
    	{
    		hoursList.push({ value: index+1, text: i.toString() + ":00pm" });
    	    hoursList.push({ value: index+2, text: i.toString() + ":30pm" });
    		
    		index += 2;
    	}	
    	
    	return hoursList;
    }
    

     

    The next step is to override the drawContent function of BaseForm:

    TimeForm.prototype.drawContent = function ()
    {
    	p.BaseForm.prototype.drawContent.call(this);
    
    	var content = this.getContent();
    .....
    }

     

    First, we create the text area:

    var row = this.row();
    	row.innerHTML = this.localInfo.subjectCaption;
    	content.appendChild(row);
    	
    	// create a text-area for the item subject
    	var textArea = this.createTextArea({ id: "subject", initValue: this.item.subject, events: { keydown: this._areaKeyDown} });
    	textArea.element.style.width = "200px";
    	this.addControl(textArea);
    
    	row = this.row();
    	row.appendChild(textArea.element);
    	content.appendChild(row);

     

    Before the text area we place a label with “Subject” on it. The createTextArea method requires several parameters. The first one is the id, then the initial content to be rendered and the events. We want the text area to be 200px wide. The row element places a row of controls in the BaseForm. We use it every time we want to start a new row with elements.

    The text area reads its initial content from the value of the subject field in the item object that we receive as reference. After that we create a new row with elements: the drop-down with the hours for the start time:

           // create a drop-down list for start hours
    	row = this.row();
    	row.innerHTML = "Start Time";
    	content.appendChild(row);
    
    	var control = this.createDropDownList({ id: "start_time", items: this.getHourLabels(), initValue: this.getStartTimeIndex(), addEmptyValue: false });
    	control.element.style.width = "200px";
    	this.addControl(control);
    
    	row = this.row();
    	row.appendChild(control.element);
    	content.appendChild(row);
    

     

    The code is almost identical to the code for the text area, but here we call the getHourLabels method that returns the array with time values. There is one other new method: getStartTimeIndex(). It’s task is to check the start time of the item and to set the initial value of the drop-down control to that time.

    Here is the method:

    // get the index of the current item's index to set the value of the startTime drop-down:
    TimeForm.prototype.getStartTimeIndex = function ()
    {
    	if (this.item != null && this.item.startTime != null)
    	{
    		
    		let index  = this.item.startTime.__getHours() * 2;
    		if(this.item.startTime.__getMinutes() > 0)
    			index++;
    		return index;		
    		
    	}
    	return -1;
    }

     

    The method checks the hour value of the start time and multiplies it by two because for each hour we show two values in the drop-down: 9:00 and 9:30. If the user has selected half an hour we increment the index with 1.
    We use almost the same code to create the endTime drop-down control, so we won’t discuss it here. You can check its code from the download with the source code files for this sample.

    Our form needs two buttons – Save and Cancel. We create them in the drawButtons method:

    // override BaseForm's drawButtons method to create form buttons
    TimeForm.prototype.drawButtons = function ()
    {
    	var thisObj = this;
    
    	var btnSave = this.createButton({
    		id: "btnSave",
    		text: this.localInfo.saveButtonCaption,
    		events: { "click": function click(e)
    		{
    			return thisObj.onSaveButtonClick(e);
    		}
    		}
    	});
    
    	var btnCancel = this.createButton({
    		id: "btnCancel",
    		text: this.localInfo.cancelButtonCaption,
    		events: { click: function click(e)
    		{
    			return thisObj.onCancelButtonClick(e);
    		}
    		}
    	});
    
    	var buttons = this.row();
    	buttons.classList.add("mfp-buttons-row");
    	buttons.appendChild(btnSave.element);
    	buttons.appendChild(btnCancel.element);
    
    	return buttons;
    };

     

    The BaseForm.createButton() method is similar to the methods that create text area and drop-down lists. We specify here that we will handle the click event for the two buttons. We place the buttons in a new row. Note that you don’t have to call the createButtons function anywhere – it is called automatically by BaseForm.

    What happens when the user presses “Save”? Well, we’ll have to read the data from the hour drop-down controls and adjust the start and end time of the newly created Item:

    TimeForm.prototype.onSaveButtonClick = function (e)
    {
    	// update the item with the form data
    	 // update the item with the form data
       var startIndex = +this.getControlValue("start_time");
       var startTime = this.item.startTime.date.clone().addHours(startIndex * 0.5);
    
       var endIndex = +this.getControlValue("end_time");
       var endTime = this.item.endTime.date.clone().addHours(endIndex * 0.5);
    
       // if end time is specified, decrease it by one day
       if (endIndex != 0 && this.item.endTime.hour == 0)
        endTime.addDays(-1);
    
       // check for inconsistent start/end time
       if (startTime.valueOf() > endTime.valueOf())
             endTime = startTime.clone().addHours(1);
    
       // apply changes 
       this.item.subject = this.getControlValue("subject"); 
       this.item.startTime = startTime;
       this.item.endTime = endTime;
    
       // if a new item is created, add it to the schedule.items collection
       if (this.type === "new")
         this.calendar.schedule.items.add(this.item);
    
       // close the form
       this.closeForm();
    
       // repaint the calendar
       this.calendar.repaint(true);
    };
    

    First, we get the value of the startTime drop-down list and we calculate the amount of time to add to the startTime of an Item. A new Item always has a startTime at midnight. Then, we check the number of days the user has selected and calculate how many hours the event actually takes. We assign end time to a copy of the start time and add the calculated event duration to the value. Finally, if the event is new – we add it to the collection of item sin the schedule, if it’s old – we correct its data. Then we close the form and repaint the calendar.

    IV. Using the Custom TimeForm

    In order to use our custom form we have to add a reference to it in the HTML page, as we did with the other two files:

     

    Then we edit the GoogleSchedule.js file First, we need to disable the built-in forms with the useForms property.

    // disable this built-in forms for item creation and modification
    calendar.useForms = false;

     

    Then we have to handle the onSelectionEnd and onItemDoubleClick events to render our form. First, we wire up the events:

    // handle the itemDoubleClick event to show the custom form for item editing
    calendar.itemDoubleClick.addEventListener(handleItemDoubleClick);
    
    // handle the selectionEnd event to show the custom form for item creation
    calendar.selectionEnd.addEventListener(handleSelectionEnd);

     

    Then we render the forms, below is the code that shows the TimeForm when a new item is created:

    function handleSelectionEnd(sender, args)
    {
    	// create a new item with the start and end time of the selection
    	var item = new p.Item();
    	item.startTime = args.startTime;
    	item.endTime = args.endTime;
    	
    	// create and show the custom form
    	var form = new TimeForm(sender, item, "new");
    	form.showForm();
    }

     

    We copy the start and end time of the selection from the event arguments. Then we use them in the constructor of the BaseForm.

    And that’s the end of this tutorial. Here is a link to download the complete source code of the sample:

    Download the Google Calendar in JavaScript Sample

    About MindFusion JavaScript Scheduler: MindFusion Js Scheduler is the complete solution for all applications that need to render interactive timetables, event schedules or appointment calendars. Fully responsive, highly customizable and easy to integrate, you can quickly program the JavaScript scheduling library according to your needs. Find out more at https://mindfusion.eu/javascript-scheduler.html