Overview

  • Use DayPilot.Scheduler.makeDraggable() to activate external DOM elements.

  • DraggableItem React component embeds the activation logic and lets you create the draggable items transparently.

  • The attached React 18 project includes a trial version of DayPilot Pro for JavaScript (see also License below).

This tutorial shows how to activate items in a custom list (plain <div> elements, ordered or unordered list, table rows). You can also use the built-in Queue component to display a queue of unscheduled tasks. The Queue React component support bi-directional drag and drop (to and from the React Scheduler), reordering list items, CSS themes. It uses an API compatible with the Scheduler component. To learn more, please see the React Work Order Planning tutorial.

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.

Creating Elements Draggable to the React Scheduler

react scheduler external drag and drop activating items

The React Scheduler component supports dragging items from an external source. The drag and drop event moving (within the Scheduler) is enabled by default and the Scheduler also accepts external items automatically. However, the external DOM elements need to be activated first using DayPilot.Scheduler.makeDraggable() method.

  • You can activate any type of DOM element (<div>, <span>, <li> and other)

  • The source element will be removed automatically on drop (this is configurable)

  • You need to define basic event properties when activating the external item: id, text and duration in seconds

Lets see how makeDraggable() method works. We will create a <div> element using JSX and get the DOM element reference using the ref attribute. As soon as the component is mounted, React will fire the hook we define in ref={}. We will use it to activate the item using makeDraggable():

<div className={"draggable-item"} ref={element => {
    if (!element) {
      return;
    }
    DayPilot.Scheduler.makeDraggable({
      element: element,
      id: this.props.id,
      text: this.props.text,
      duration: this.props.days*24*60*60,
    })
  }}>{this.props.text}</div>

Note that we have defined element, id, text, and duration options - these are required.

It's important to remember that the ref hook is called twice - when the component is mounted (element holds the DOM element object) and when the component is unmounted (element is null). We are only calling our logic during mounting.

DraggableItem React Component

react scheduler external drag and drop item component jsx

In order to make the code cleaner, we will wrap the activation logic in a special DraggableItem React component. We will use the same logic as in the previous example but we will load the variable value from the component props.

import React, {Component} from 'react';
import {DayPilot} from "daypilot-pro-react";

class DraggableItem extends Component {

  render() {
    return (<div className={"draggable-item"} ref={element => {
      if (!element) {
        return;
      }
      DayPilot.Scheduler.makeDraggable({
        element: element,
        id: this.props.id,
        text: this.props.text,
        duration: this.props.days*24*60*60,
        onDragStart: args => {
          args.options.element.classList.add("draggable-source");
        },
        onDrop: args => {
          args.options.element.classList.remove("draggable-source");
        }
      })
    }}>{this.props.text}</div>);
  }

}

export default DraggableItem;

As you can see, the component accepts three properties:

  • id - ID of the event

  • text - text of the event

  • days - duration in days

We are using "Day" scale in the Scheduler component and the duration of the items will is defined in days as well. We calculate the seconds from the days automatically in the DraggableItem component.

The DraggableItem React component defines onDragStart and onDrop event handlers using DayPilot.Scheduler.makeDraggable() method - these events will let us highlight the source item during dragging.

Now we can use the DraggableItem component to define the draggable items easily:

<DraggableItem id={101} text={"Item #101"} days={1}></DraggableItem>
<DraggableItem id={102} text={"Item #102"} days={2}></DraggableItem>
<DraggableItem id={103} text={"Item #103"} days={3}></DraggableItem>

Handling the External Item Drop in the Scheduler

react scheduler external drag and drop over

As soon as the user drops the item in the Scheduler, standard onEventMoved event will be fired. You can test if the item was dragged from the external source by checking args.external value:

this.state = {
  // ...
  onEventMoved: function (args) {
    this.message("Event moved: " + args.e.text() + ", external: " + args.external);
  },

};

The Scheduler automatically detects that this is a new event, adds it to the internal store and displays it:

react scheduler external drag and drop target position

Full Source Code

src/scheduler/Scheduler.js

import React, {Component} from 'react';
import {DayPilot, DayPilotScheduler} from "daypilot-pro-react";
import DraggableItem from "./DraggableItem";

class Scheduler extends Component {

  constructor(props) {
    super(props);

    this.state = {
      timeHeaders: [{"groupBy": "Month"}, {"groupBy": "Day", "format": "d"}],
      scale: "Day",
      days: DayPilot.Date.today().daysInMonth(),
      startDate: DayPilot.Date.today().firstDayOfMonth(),
      onEventMoved: function (args) {
        this.message("Event moved: " + args.e.text() + ", external: " + args.external);
      },
      treeEnabled: true,
    };
  }

  componentDidMount() {

    // load resource and event data
    this.setState({
      resources: [
        {name: "Resource A", id: "A"},
        {name: "Resource B", id: "B"},
        {name: "Resource C", id: "C"},
        {name: "Resource D", id: "D"},
        {name: "Resource E", id: "E"},
        {name: "Resource F", id: "F"},
        {name: "Resource G", id: "G"}
      ],
      events: []
    });

  }

  render() {
    let {...config} = this.state;
    return (
      <div style={{display: "flex", marginBottom: "30px"}}>

        <div className={"draggable-container"}>
          <div className={"draggable-header"}>Drag items to the Scheduler:</div>

          <DraggableItem id={101} text={"Item #101"} days={1}></DraggableItem>
          <DraggableItem id={102} text={"Item #102"} days={2}></DraggableItem>
          <DraggableItem id={103} text={"Item #103"} days={3}></DraggableItem>

        </div>

        <div style={{flex: 1}}>
          <DayPilotScheduler {...config} ref={component => this.scheduler = component && component.control }/>
        </div>

      < /div>
    );
  }
}

export default Scheduler;

src/scheduler/DraggableItem.js

import React, {Component} from 'react';
import {DayPilot} from "daypilot-pro-react";

class DraggableItem extends Component {

  render() {
    return (<div className={"draggable-item"} ref={element => {
      if (!element) {
        return;
      }
      DayPilot.Scheduler.makeDraggable({
        element: element,
        id: this.props.id,
        text: this.props.text,
        duration: this.props.days*24*60*60,
        onDragStart: args => {
          args.options.element.classList.add("draggable-source");
        },
        onDrop: args => {
          args.options.element.classList.remove("draggable-source");
        }
      })
    }}>{this.props.text}</div>);
  }
}

export default DraggableItem;

App.css (global CSS)

#root {
    --color-event-background: #3d85c6;
    --color-event-text: #ffffff;
}

.draggable-container {
    width: 200px;
    padding: 0px 5px;
}

.draggable-header {
    border: 1px solid #ccc;
    padding: 5px;
    background: #f0f0f0;
    margin-bottom: 15px;
}

.draggable-item {
    border-radius: 20px;
    background: var(--color-event-background);
    color: var(--color-event-text);
    padding-left: 10px;
    border: 0px;

    height: 35px;
    display: flex;
    align-items: center;

    cursor: move;
    margin-bottom: 5px;
}


.draggable-source {
    opacity: 0.5;
}


#root .scheduler_default_event_inner {
    border-radius: 20px;
    background: var(--color-event-background);
    color: var(--color-event-text);
    padding-left: 10px;
    border: 0px;
}

History

  • October 10, 2022: Upgraded to React 18, DayPilot Pro 2022.4.5442. Updated CSS.

  • December 16, 2020: Upgraded to React 17, DayPilot Pro 2020.4.4807

  • October 2, 2019: Initial release, React 16.