Features

  • Split Scheduler events at a specified location using a context menu

  • Optional snap-to-grid mode splits the event at the cell start

License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript.

HTML5 Scheduler Config

html5 scheduler events splitting configuration

This is the starting configuration of HTML5/JavaScript Scheduler component generated using Scheduler UI Builder. In the next steps, we will extend it with the splitting functionality.

<div id="dp"></div>

<script>
  var dp = new DayPilot.Scheduler("dp", {
    timeHeaders: [{"groupBy":"Month"},{"groupBy":"Day","format":"d"}],
    scale: "Day",
    days: DayPilot.Date.today().daysInMonth(),
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    // ...
  });
  dp.resources = [
    {name: "Resource 1", id: "R1"},
    {name: "Resource 2", id: "R2"},
    {name: "Resource 3", id: "R3"},
    {name: "Resource 4", id: "R4"},
    {name: "Resource 5", id: "R5"},
    {name: "Resource 6", id: "R6"},
  ];
  dp.events.list = [
    {
      text: "Event 1",
      start: DayPilot.Date.today().firstDayOfMonth().addDays(1),
      end: DayPilot.Date.today().firstDayOfMonth().addDays(6),
      resource: "R1",
      id: 1
    }
  ];
  dp.init();
</script>

HTML5 Scheduler: Current Mouse Position (in Pixels)

The current mouse position in pixels (relative to the Scheduler grid) is accessible using getCoords() method:

var position = dp.getCoords().x;

HTML5 Scheduler: Current Mouse Position (as Date/Time)

We can use getDate() method to convert the pixel position to date/time value:

var position = dp.getDate(dp.getCoords().x, true);

The second parameter specifies whether an exact position should be calculated. If you use false value it will return the start of the current grid cell.

var position = dp.getDate(dp.getCoords().x, false);

Splitting Events using Context Menu

html5 scheduler event splitting context menu javascript

In this step, we will add a context menu that will let users split an event into two new events at a specified location:

html5 scheduler event splitting after

The context menu only has a single item ("Split"). It calculates the current date/time position, updates the event with a new end and creates another event starting at this time point:

var dp = new DayPilot.Scheduler("dp", {
  // ...
  contextMenu: new DayPilot.Menu({
    onShow: function(args) {
      dp.contextMenu._x = dp.getCoords().x;
    },
    items: [
      {text: "Split", onClick: function(args) {
        var time = dp.getDate(dp.contextMenu._x, true);
        var e = args.source;
        var originalEnd = e.end();

        e.data.end = time;
        dp.events.update(e);

        var newE = {
          start: time,
          end: originalEnd,
          id: DayPilot.guid(),
          resource: e.resource(),
          text: e.text() + ", part 2"
        };

        dp.events.add(newE);
      }
      }
    ]
  })
});

Snap-To-Grid Mode

html5 scheduler event splitting snap to grid

We will also add a "Snap-to-grid" checkbox which will activate the snap-to-grid mode of the Scheduler. When this option is checked, the event will be split at the start of the current grid cell:

html5 scheduler event splitting snap to grid after

Source code:

<div class="main">
  <div class="space">
    <label><input type="checkbox" id="snap"> Snap-to-grid</label>
  </div>
  <div id="dp"></div>
</div>

<script>
  var elements = {
    snap: document.getElementById("snap")
  };

  elements.snap.addEventListener("click", function(ev) {
    var enabled = elements.snap.checked;
    dp.useEventBoxes = enabled ? "Always" : "Never";
    dp.snapToGrid = enabled;
    dp.update();
  });

  var dp = new DayPilot.Scheduler("dp", {
    cellWidth: 40,
    timeHeaders: [{"groupBy":"Month"},{"groupBy":"Day","format":"d"}],
    scale: "Day",
    days: DayPilot.Date.today().daysInMonth(),
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    eventHeight: 30,
    headerHeight: 30,
    useEventBoxes: elements.snap.checked ? "Always" : "Never",
    snapToGrid: elements.snap.checked,
    onTimeRangeSelected: function (args) {
      var dp = this;
      DayPilot.Modal.prompt("Create a new event:", "Event 1").then(function(modal) {
        dp.clearSelection();
        if (modal.canceled) { return; }
        dp.events.add({
          start: args.start,
          end: args.end,
          id: DayPilot.guid(),
          resource: args.resource,
          text: modal.result
        });
      });
    },
    contextMenu: new DayPilot.Menu({
      onShow: function(args) {
        dp.contextMenu._x = dp.getCoords().x;
      },
      items: [
        {text: "Split", onClick: function(args) {
          var time = dp.getDate(dp.contextMenu._x, !elements.snap.checked);
          var e = args.source;
          var originalEnd = e.end();

          e.data.end = time;
          dp.events.update(e);

          var newE = {
            start: time,
            end: originalEnd,
            id: DayPilot.guid(),
            resource: e.resource(),
            text: e.text() + ", part 2"
          };

          dp.events.add(newE);
        }
        }
      ]
    })
  });
  dp.resources = [
    {name: "Resource 1", id: "R1"},
    {name: "Resource 2", id: "R2"},
    {name: "Resource 3", id: "R3"},
    {name: "Resource 4", id: "R4"},
    {name: "Resource 5", id: "R5"},
    {name: "Resource 6", id: "R6"},
  ];
  dp.events.list = [
    {
      text: "Event 1",
      start: DayPilot.Date.today().firstDayOfMonth().addDays(1),
      end: DayPilot.Date.today().firstDayOfMonth().addDays(6),
      resource: "R1",
      id: 1
    }
  ];
  dp.init();
</script>