Overview

  • Add a context menu to JavaScript Scheduler events with “Lock” and “Unlock” items.

  • Users will not be able to move or change the locked events

  • The context menu is updated dynamically depending on the event status.

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

Context Menu with “Lock” Item

javascript-scheduler-lock-events-context-menu-item.png

Lets define a context menu that will allow users lock the Scheduler event:

var dp = new DayPilot.Scheduler("dp", {
  contextMenu: new DayPilot.Menu({
    items: [
      { text: "Lock",
        onClick: function lockToggle(args) {
          var e = args.source;
          var locked = e.data.tags && e.data.tags.locked;
          if (!e.data.tags) {
            e.data.tags = {};
          }
          e.data.tags.locked = !locked;
          dp.events.update(e);
        }
      },
      { text: "-"},
      { text: "Edit...",
        onClick: function (args) {
          editEvent(args.source);
        }
      },
    ],
  }),
});

The onClick event handler of the “Lock” menu item sets the locked custom property to true. The custom property is saved under tags object of the event data object to prevent naming conflicts.

Locking the JavaScript Scheduler Event

We will lock the item using onBeforeEventRender.

var dp = new DayPilot.Scheduler("dp", {

  // ...
  
  onBeforeEventRender: function(args) {
    var locked = args.data.tags && args.data.tags.locked;
    if (locked) {
      args.data.moveDisabled = true;
      args.data.resizeDisabled = true;
      args.data.clickDisabled = true;
    }
  },
  
  // ...
  
});

If the “locked” status is detected (args.data.tags.locked) the drag and drop event operations (moving, resizing) are disabled, as well as the click action.

For more options on making the Scheduler events (or the whole Scheduler) read-only please see the following tutorial:

Locked Event Icon

javascript-scheduler-locked-event-icon.png

We will also use onBeforeEventRender event handler to display a padlock icon inside the locked event:

var dp = new DayPilot.Scheduler("dp", {

  // ...
  
  onBeforeEventRender: function(args) {
    var locked = args.data.tags && args.data.tags.locked;
    if (locked) {
    
      // ...
    
      args.data.areas = [
        { top: 10, right: 4, width: 15, height: 15, image: "padlock-15x15.svg", xhtml: "🚫"}
      ];
    }
  },
  
  // ...
  
});

Unlocking the Scheduler Event

javascript-scheduler-unlock-event.png

We will use the onShow event of the context menu object to change the context menu items depending on the event status.

If the event is locked, the following changes will be made:

  • The first menu item (“Lock”) text will be changed to “Unlock”

  • The “Edit…” item will be disabled

contextMenu: new DayPilot.Menu({
  items: [
    { text: "Lock",
      onClick: function lockToggle(args) {
        var e = args.source;
        var locked = e.data.tags && e.data.tags.locked;
        if (!e.data.tags) {
          e.data.tags = {};
        }
        e.data.tags.locked = !locked;
        dp.events.update(e);
      }
    },
    { text: "-"},
    { text: "Edit...",
      onClick: function (args) {
        editEvent(args.source);
      }
    },
  ],
  onShow: function(args) {
    var e = args.source;
    var locked = e.data.tags && e.data.tags.locked;
    dp.contextMenu.items[0].text = locked ? "Unlock" : "Lock";
    dp.contextMenu.items[2].disabled = locked;
  }
}),

Full Source Code

This is the full source code of our example that adds a context menu to the JavaScript Scheduler events with an option to lock the event.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>JavaScript Scheduler: Lock Events using Context Menu</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/30746/javascript-scheduler-lock-events-using-context-menu'>JavaScript Scheduler: Lock Events using Context Menu</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 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",
    days: 31,
    startDate: "2021-08-01",
    timeRangeSelectedHandling: "Enabled",
    onTimeRangeSelected: function (args) {
      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
        }));
      });
    },
    onBeforeEventRender: function(args) {
      var locked = args.data.tags && args.data.tags.locked;
      if (locked) {
        args.data.moveDisabled = true;
        args.data.resizeDisabled = true;
        args.data.clickDisabled = true;
        args.data.areas = [
          { top: 10, right: 4, width: 15, height: 15, image: "padlock-15x15.svg", xhtml: "&#x1F6AB;"}
        ];
      }
    },
    onEventClick: function(args) {
      editEvent(args.e);
    },
    contextMenu: new DayPilot.Menu({
      items: [
        { text: "Lock",
          onClick: function lockToggle(args) {
            var e = args.source;
            var locked = e.data.tags && e.data.tags.locked;
            if (!e.data.tags) {
              e.data.tags = {};
            }
            e.data.tags.locked = !locked;
            dp.events.update(e);
          }
        },
        { text: "-"},
        { text: "Edit...",
          onClick: function (args) {
            editEvent(args.source);
          }
        },
      ],
      onShow: function(args) {
        var e = args.source;
        var locked = e.data.tags && e.data.tags.locked;
        dp.contextMenu.items[0].text = locked ? "Unlock" : "Lock";
        dp.contextMenu.items[2].disabled = locked;
      }
    }),
    treeEnabled: true,
  });
  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"},
    {name: "Resource 7", id: "R7"},
    {name: "Resource 8", id: "R8"},
    {name: "Resource 9", id: "R9"},
  ];
  dp.events.list = [
    {id: 1, start: "2021-08-05T00:00:00", end: "2021-08-10T00:00:00", text: "Event 1", resource: "R2"}
  ];
  dp.init();


  function editEvent(e) {
    var form = [
      {name: "Name", id: "text"}
    ];
    var data = e.data;

    DayPilot.Modal.form(form, data).then(function(modal) {
      if (modal.canceled) {
        return;
      }
      e.data = modal.result;
      dp.events.update(e);
    });
  }
</script>

</body>
</html>