Overview

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. Buy a license.

Add a Frozen Row to the JavaScript Scheduler

javascript-scheduler-frozen-rows-select-time-columns.png

Add a frozen row to the top of the JavaScript Scheduler grid using resources array:

dp.resources = [
  {name: "Select Days", id: "S", frozen: "Top"},
  {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"},
];

The frozen row will be displayed at the top of the grid and it will become part of the time header. It will stay visible even if the user scrolls down.

Expand Time Range Selection to the Full Column

javascript-scheduler-frozen-rows-expand-selection.png

We will use onTimeRangeSelecting event (which is fired in real time during time range selection) to expand the selection to the whole column.

First, we detect if the selection is being created in the special frozen row:

onTimeRangeSelecting: function(args) {
  if (args.resource === "S") {
    // ...
  }
},

In that case, we expand the selection to all rows using the selection API:

onTimeRangeSelecting: function(args) {
  if (args.resource === "S") {
    dp.multirange.clear();
    dp.resources.forEach(function(r) {
      if (r.frozen) {
        return;
      }
      dp.multirange.add({
        start: args.start,
        end: args.end,
        resource: r.id
      });
    });
  }
},

When the user completes the selection (releases the mouse button) the Scheduler fires onTimeRangeSelected event handler. Normally, this event handler is used to create a new event. We need to modify the event handler and check if the selection was created in the frozen row:

onTimeRangeSelected: function (args) {
  if (args.resource === "S") {
    // column selection complete
    return;
  }
  var dp = this;
  DayPilot.Modal.prompt("Create a new event:", "Event 1").then(function(modal) {
    dp.clearSelection();
    if (!modal.result) { return; }
    dp.events.add(new DayPilot.Event({
      start: args.start,
      end: args.end,
      id: DayPilot.guid(),
      resource: args.resource,
      text: modal.result
    }));
  });
},

In this example, we simply keep the selection visible (no clearSelection() call) but this is the place where you wan to define your own action.

Optional: Make It Look Like the Time Header

javascript-scheduler-frozen-rows-time-header.png

So far, we were using two time header rows:

  • month

  • day of month

timeHeaders: [
  {groupBy:"Month"},
  {groupBy:"Day", format:"d"}
],

In the following example, we will hide the second time header row and display the day of month number in our special frozen row.

First, we need to remove the second row from the time header definition:

timeHeaders: [
  {groupBy:"Month"},
  // {groupBy:"Day", format:"d"}
],

And we add an onBeforeCellRender event handler that will change the appearance of cells in the frozen rows. We add a day number and a hover highlight using active areas.

onBeforeCellRender: function(args) {
  var r = dp.rows.find(args.cell.resource);
  if (r.id === "S") {
    args.cell.backColor = "#f3f3f3";
    args.cell.areas = [
      {left:0, top: 0, right: 0, bottom: 0, style: "display: flex; justify-content: center; align-items: center;", html: args.cell.start.toString("d")},
      {left:0, top: 0, right: 0, bottom: 0, style: "opacity: 0.2; background-color: gray;", visibility: "Hover"}
    ];
  }
},

Full Source Code

This is the full HTML5/JavaScript source code of the JavaScript Scheduler configured to select multiple time columns using drag and drop:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>JavaScript Scheduler: Use Frozen Rows to Select Time Columns</title>

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

  <!-- DayPilot library -->
  <script src="js/daypilot/daypilot-all.min.js"></script>

  <style>
    button {
      background-color: #f3f3f3;
      border: 1px solid #c0c0c0;
      color: #333;
      padding: 8px 0px;
      width: 100px;
      border-radius: 2px;
      cursor: pointer;
      margin-right: 5px;
    }
  </style>

</head>
<body>
<div class="header">
  <h1><a href='https://code.daypilot.org/50252/javascript-scheduler-use-frozen-rows-to-select-time-columns'>JavaScript Scheduler: Use Frozen Rows to Select Time Columns</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">
    <button id="clear">Clear selection</button>
  </div>
  <div id="dp"></div>
  <div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>
</div>

<script>
  var dp = new DayPilot.Scheduler("dp", {
    timeHeaders: [
      {groupBy:"Month"},
      // {groupBy:"Day", format:"d"}
    ],
    scale: "Day",
    heightSpec: "Max",
    height: 300,
    days: 365,
    startDate: "2021-01-01",
    timeRangeSelectedHandling: "Enabled",
    onTimeRangeSelected: function (args) {
      if (args.resource === "S") {
        return;
      }
      var dp = this;
      DayPilot.Modal.prompt("Create a new event:", "Event 1").then(function(modal) {
        dp.clearSelection();
        if (!modal.result) { return; }
        dp.events.add(new DayPilot.Event({
          start: args.start,
          end: args.end,
          id: DayPilot.guid(),
          resource: args.resource,
          text: modal.result
        }));
      });
    },

    onTimeRangeSelecting: function(args) {
      if (args.resource === "S") {
        dp.multirange.clear();
        dp.resources.forEach(function(r) {
          if (r.frozen) {
            return;
          }
          dp.multirange.add({
            start: args.start,
            end: args.end,
            resource: r.id
          });
        });
      }
    },
    onBeforeCellRender: function(args) {
      var r = dp.rows.find(args.cell.resource);
      if (r.id === "S") {
        args.cell.backColor = "#f3f3f3";
        args.cell.areas = [
          {left:0, top: 0, right: 0, bottom: 0, style: "display: flex; justify-content: center; align-items: center;", html: args.cell.start.toString("d")},
          {left:0, top: 0, right: 0, bottom: 0, style: "opacity: 0.2; background-color: gray;", visibility: "Hover"}
        ];
      }
    },
    crosshairType: "Disabled"
  });
  dp.resources = [
    {name: "Select Days", id: "S", frozen: "Top"},
    {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"},
  ];
  dp.events.list = [];
  dp.init();

  document.querySelector("#clear").addEventListener("click", function() {
    dp.multirange.clear();
  })
</script>

</body>
</html>