
| Demo | |
|---|---|
| Downloads | |
| Repository | |
| Languages | JavaScript |
| Code License | Apache License 2.0 (Open-Source) |
Overview
Use HTML5 drag and drop API to drag items from events to a target outside of the JavaScript Scheduler component:
-
Add one or more icons to the event (using active areas) and make them draggable.
-
Use a custom drop target.
-
Make the event data accessible to the drop event handler.
The sample project uses the open-source DayPilot Lite for JavaScript (see License below).
To run the npm project, use npm install followed by npm run dev. Use npm run build to create a production build. The sample was generated using DayPilot UI Builder.
License
The sample project uses DayPilot Lite for JavaScript, licensed under the Apache License 2.0.
Add Draggable Items to Scheduler Events
We will use event active areas to add the items. This is convenient because it lets us specify the position and styling easily, but you can use plain HTML as well.
Let’s add a simple green square to the right side of the event using onBeforeEventRender:
onBeforeEventRender: (args) => {
args.data.areas = [
{
right: 5,
top: 10,
height: 15,
width: 15,
backColor: "#6aa84f",
cssClass: "draggable",
style: "cursor: move"
}
];
},
As soon as the event is rendered, we can make the element draggable. We have marked the active area using the "draggable" CSS class so we can find and activate it in the onAfterEventRender event handler:
onAfterEventRender: (args) => {
const area = args.div.getElementsByClassName("draggable")[0];
area.setAttribute("draggable", "true");
area.addEventListener("dragstart", (ev) => {
ev.dataTransfer.setData("daypilot/external-item", JSON.stringify(args.e.data));
ev.dataTransfer.effectAllowed = "copy";
});
area.addEventListener("mousedown", (ev) => {
ev.stopPropagation();
});
}
We have attached the event data object (serialized as a JSON string) to the dragged object using the ev.dataTransfer.setData() method. The custom daypilot/external-item type identifies the payload, and effectAllowed = "copy" keeps the drag operation a copy.
We also need to stop the propagation of the "mousedown" event to prevent event moving (which is enabled by default; we want to keep it enabled for the rest of the event box).
Adding the External Drop Target
Our drop target is a simple <div> element:
<div id="drop">You can drop the green square from events here.</div>
We will add some styling using CSS to highlight it:
#drop {
min-height: 100px;
box-sizing: border-box;
border: 1px dashed #8bb99b;
border-radius: 6px;
background-color: #f5faf7;
color: #3d624b;
padding: 18px;
margin: 0 0 20px;
line-height: 22px;
overflow-wrap: anywhere;
}
The drop target needs to be activated as well. The app.init() method calls initDropTarget() after loading the Scheduler data. In dragover, check the custom data type and call preventDefault() to allow a drop. Set dropEffect to "copy" so the browser shows the intended operation:
const app = {
initDropTarget() {
const drop = document.getElementById("drop");
drop.ondragover = (ev) => {
if (!ev.dataTransfer.types.includes("daypilot/external-item")) {
return;
}
ev.preventDefault();
ev.dataTransfer.dropEffect = "copy";
};
drop.ondrop = (ev) => {
const data = ev.dataTransfer.getData("daypilot/external-item");
if (!data) {
return;
}
ev.preventDefault();
try {
const event = JSON.parse(data);
console.log("drop data", event);
drop.innerText = data;
} catch {
drop.innerText = "The dropped item does not contain valid event data.";
}
};
},
// ...
};
In the "drop" event handler, we can access the original event data using the DataTransfer.getData() method.
In our event handler, we deserialize the object using JSON.parse() and print it to the console. In addition to that, the full source data string is displayed in the drop target using innerText. The source event stays in the Scheduler; this copies its data rather than moving or deleting it. Unrelated drops are ignored, and malformed JSON produces a message instead of an uncaught error.
Full Source Code
The complete src/app.js module is shown below. The Scheduler displays the current month, with Event 1 on Resource 2 from day 3 up to (but not including) day 10. The page markup and styling are in index.html.
import {DayPilot} from "@daypilot/daypilot-lite-javascript";
const dp = new DayPilot.Scheduler("dp", {
timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "d"}],
scale: "Day",
rowHeaderWidth: 90,
days: DayPilot.Date.today().daysInMonth(),
startDate: DayPilot.Date.today().firstDayOfMonth(),
onTimeRangeSelected: async (args) => {
const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
dp.clearSelection();
if (modal.canceled || !modal.result) {
return;
}
dp.events.add({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: modal.result
});
},
onBeforeEventRender: (args) => {
args.data.areas = [
{
right: 5,
top: 10,
height: 15,
width: 15,
backColor: "#6aa84f",
cssClass: "draggable",
style: "cursor: move"
}
];
},
onAfterEventRender: (args) => {
const area = args.div.getElementsByClassName("draggable")[0];
area.setAttribute("draggable", "true");
area.addEventListener("dragstart", (ev) => {
ev.dataTransfer.setData("daypilot/external-item", JSON.stringify(args.e.data));
ev.dataTransfer.effectAllowed = "copy";
});
area.addEventListener("mousedown", (ev) => {
ev.stopPropagation();
});
}
});
dp.init();
const app = {
initDropTarget() {
const drop = document.getElementById("drop");
drop.ondragover = (ev) => {
if (!ev.dataTransfer.types.includes("daypilot/external-item")) {
return;
}
ev.preventDefault();
ev.dataTransfer.dropEffect = "copy";
};
drop.ondrop = (ev) => {
const data = ev.dataTransfer.getData("daypilot/external-item");
if (!data) {
return;
}
ev.preventDefault();
try {
const event = JSON.parse(data);
console.log("drop data", event);
drop.innerText = data;
} catch {
drop.innerText = "The dropped item does not contain valid event data.";
}
};
},
init() {
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"}
];
const events = [
{
start: dp.startDate.addDays(2),
end: dp.startDate.addDays(9),
id: 1,
text: "Event 1",
resource: "R2"
}
];
dp.update({resources, events});
this.initDropTarget();
}
};
app.init();
DayPilot




