Overview

  • How to configure the JavaScript Scheduler to allow a time range selection over multiple adjacent rows.

  • Based on rectangle selection feature.

  • Requires DayPilot Pro for JavaScript 2020.2.4488 or higher

  • Includes a trial version of DayPilot Pro for JavaScript (see License below)

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.

Live Demo

JavaScript Scheduler: Rectangle Selection

javascript scheduler time range rows rectangle

In addition to the standard drag and drop operations, the Scheduler supports selecting a custom rectangle area. It has to be enabled using rectangleSelectHandling property.

Selecting Events using the Rectangle

It can be used to select all events in the rectangle (set rectangleSelectHandling to "EventSelect").

Shift + drag will activate the selecting and the corresponding rectangle will be displayed:

javascript scheduler time range rows rectangle event selection

The underlying events will be selected when you release the mouse button:

javascript scheduler time range rows rectangle event selection complete

Selecting a Time Range

We will use this feature to create a custom time range selection that spans multiple adjacent rows.

First, we enable the rectangle selection, without the default action:

rectangleSelectHandling: "Enabled"

Then we need to create an onRectangleSelecting event handler that will create/update the time range multi-selection:

onRectangleSelecting: (args) => {
  dp.multirange.clear();
  args.resources.forEach((r) => {
    dp.multirange.add({start: args.start, end: args.end, resource: r});
  });
},

We can hide the rectangle by adding args.visible = true; to the event handler:

onRectangleSelecting: (args) => {
  args.visible = false;
  // ...
},

Handling the Multi-Row Selection

javascript scheduler time range rows selection

Normally, you would use onTimeRangeSelect event to handle time-range selections. In this case, this event is not fired because we are creating the time range using the API.

Instead, we will use onRectangleSelect event handler which is fired when the rectangle selection is complete (when the user releases the mouse button).

onRectangleSelect: (args) => {
  dp.multirange.get().forEach((range) => {
    if (!range.allowed) {
      return;
    }
    dp.events.add({
      start: range.start,
      end: range.end,
      resource: range.resource,
      text: "Event",
      id: DayPilot.guid()
    })
  });

  dp.multirange.clear();
}

In this example, we create a new event in rows where it is allowed (range.allowed == true) and clear the selection.

Full Source Code

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>JavaScript Scheduler: Select a Time Range over Multiple Rows</title>

  <style type="text/css">
    <!-- ... -->
  </style>

  <!-- DayPilot library -->
  <script src="js/daypilot/daypilot-all.min.js"></script>
</head>
<body>
<div class="header">
  <h1><a href='https://code.daypilot.org/85404/javascript-scheduler-select-a-time-range-over-multiple-rows'>JavaScript Scheduler: Select a Time Range over Multiple Rows</a></h1>
  <div><a href="https://javascript.daypilot.org/">DayPilot for JavaScript</a> - HTML5 Calendar/Scheduling Components for JavaScript/Angular/React/Vue</div>
</div>

<div class="main">
  <div class="space">Use Shift+drag to select a time range for multiple rows.</div>
  <div id="dp"></div>
  <div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>
</div>

<script>
  const dp = new DayPilot.Scheduler("dp", {
    timeHeaders: [{"groupBy":"Month"},{"groupBy":"Day","format":"d"}],
    scale: "Day",
    days: DayPilot.Date.today().daysInMonth(),
    startDate: DayPilot.Date.today().firstDayOfMonth(),
    timeRangeSelectedHandling: "Enabled",
    onTimeRangeSelected: async (args) => {
      const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
      dp.clearSelection();
      if (modal.canceled) {
        return;
      }
      dp.events.add({
        start: args.start,
        end: args.end,
        id: DayPilot.guid(),
        resource: args.resource,
        text: modal.result
      });
    },
    rectangleSelectMode: "Free",
    rectangleSelectHandling: "EventSelect",
    allowEventOverlap: false,
    onRectangleSelecting: (args) => {
      args.visible = false;
      dp.multirange.clear();
      args.resources.forEach((r) => {
        dp.multirange.add({start: args.start, end: args.end, resource: r});
      });
    },
    onRectangleSelect: (args) => {
      dp.multirange.get().forEach((range) => {
        if (!range.allowed) {
          return;
        }
        dp.events.add({
          start: range.start,
          end: range.end,
          resource: range.resource,
          text: "Event",
          id: DayPilot.guid()
        })
      });

      dp.multirange.clear();
    }
  });
  dp.init();

  const app = {
    loadData() {
      const 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"},
        {name: "Resource 7", id: "R7"},
        {name: "Resource 8", id: "R8"},
        {name: "Resource 9", id: "R9"},
      ];
      const events = [
        { text: "Event 1", start: "2020-06-12T00:00:00", end: "2020-06-17T00:00:00", resource: "R3", id: 1},
        { text: "Event 2", start: "2020-06-16T00:00:00", end: "2020-06-21T00:00:00", resource: "R5", id: 2},
        { text: "Event 3", start: "2020-06-10T00:00:00", end: "2020-06-16T00:00:00", resource: "R7", id: 3},
      ];
      dp.update({resources, events});
    }
  };
  app.loadData();

</script>

</body>
</html>