user3796642
user3796642

Reputation: 169

FullCalendar Horizontal Time Range Selection

Can someone tell me how to achieve something like below image. How can I select particular time range in selected days.

Notice that the selection spans multiple days. It's a horizontal selection across multiple days (as opposed to a continuous one).

I am using fullCalendar jquery library.

intended result

Upvotes: 6

Views: 5118

Answers (2)

deepsky
deepsky

Reputation: 875

I ran into the same requirement recently. After digging on the Internet, no perfect solution was found. The above solution resolved the rendering problem but has some other issues, e.g. dragging issue, event ID.

So I forked the original fullCalendar repo on github and extended it with horizontal selection support.

DEMO

Usage:

Please replace the js file and set options as below:

(expandThrough is added. false means horizontal selection is on)

...
selectable: true,
selectOverlap: false,
eventOverlap: false,
editable: true,
expandThrough: false,
...

Javascript dist file:

https://github.com/deepskyr/fullcalendar/blob/horizontal_range_selection/dist/fullcalendar.js

Upvotes: 4

DanielST
DanielST

Reputation: 14163

So, the exact thing you need to do isn't quite possible unless you want to heavily modify FC. But you can do something quite close:

horizontal FC selection

JSFiddle

Basic Algorithm

  • Turn on selectHelper so that FC will attempt to render it as an event.
  • In eventRender, stop the helper from actually rendering. Instead, take its start and end dates and chunk them into one event per day.
  • Render the chunked events.

Chunking and Rendering

// Chunks a multiday event into an array of events
// i.e. From {start:"2015-05-06T11:00",end:"2015-05-08T15:00"}
//      into [ {start:"2015-05-06T11:00",end:"2015-05-07T15:00"},
//             {start:"2015-05-07T11:00",end:"2015-05-08T15:00"} ]
var chunk = function (event,type) {
    var chunked = [];
        //diff = moment.duration(event.end.diff(event.start));
    if (event.start.format('HHmm') > event.end.format('HHmm')) {
        return false; //Can't chunk, starttime > endtime
    }
    for (var day = event.start.clone(); !day.isAfter(event.end,'day'); day.add(1,'day')) {
        chunked.push({
            start:day.clone(),
            end:day.clone().hour(event.end.hour()).minute(event.end.minute()),
            id:"chunked-"+type //Used as a flag in the render function
        })
    }
    return chunked;
};
// Takes an event and renders it chunked. Also remove the previous chunked-helper.
// Runs after a timeout and only once.
var renderChunkedHelper = (function(){
    var id = 0;
    return function(event){
        window.clearTimeout(id);
        id = window.setTimeout(function(){
            var chunked = chunk(event,"helper");
            eventToChunk = null;
            $("#calendar").fullCalendar( "removeEvents", "chunked-helper");
            for(var i = 0; i < chunked.length; i++){
                $("#calendar").fullCalendar("renderEvent", chunked[i]); //Manually render each chunk
            }
        },0); //delay in ms. Could be tweaked for optimal perfomance

    }
})();

FC options

$("#calendar").fullCalendar({
    /*...*/
    selectable: true,
    selectHelper: true,
    select: function( start, end, jsEvent, view ){
        if(window.confirm("Create this event?")){
            $("#calendar").fullCalendar( "removeEvents", "chunked-helper");
            $("#calendar").fullCalendar( "addEventSource",chunk({start:start,end:end},"event"));
        }else{
            $("#calendar").fullCalendar( "removeEvents", "chunked-helper");
        }
    },
    eventRender: function (event,element) {
        if(event.className[0] === "fc-helper"){ //if it's the drag event
            renderChunkedHelper(event);
            return false; //don't actually render the select helper
        }
    }
});

Upvotes: 6

Related Questions