Overview

The React Scheduler component from DayPilot Pro for JavaScript displays a timeline for the specified resources. The resources are displayed as rows and the time slots are displayed as columns.

In this tutorial, we will focus on add UI controls to the rows that represent resources (buildings, people, tools, cars, machines) that will let users of your React application manage the resources - add, move, delete and edit them.

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.

How to load Scheduler resource data in React?

react scheduler how to load resource data

As soon as you have the React Scheduler installed and configured (see React Scheduler Component Tutorial for an introduction), you can load the Scheduler data.

The following example loads the resource data in the componentDidMount() method:

componentDidMount() {
  
  const resources = [
    {name: "Group A", id: "GroupA", expanded: true, children: [
        {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"}
      ]
    },
    {name: "Group B", id: "GroupB", expanded: true, children: [
        {name: "Resource H", id: "H"},
        {name: "Resource I", id: "I"},
      ]}
  ];

  this.scheduler.update({
    resources
  });

}

Instead of using the update() method of the React Scheduler, you can also add the resources to the state and the component will update automatically.

In this example, the state content is automatically expanded to React Scheduler properties using the spread syntax.

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

class Scheduler extends Component {

  constructor(props) {
    super(props);

    this.state = {
      treeEnabled: true, 
      // ...
    };
  }

  componentDidMount() {

    const resources = [
      {name: "Group A", id: "GroupA", expanded: true, children: [
          {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"}
        ]
      },
      {name: "Group B", id: "GroupB", expanded: true, children: [
          {name: "Resource H", id: "H"},
          {name: "Resource I", id: "I"},
        ]}
    ];
    
    this.setState({resources});

  }

  render() {
    var {...config} = this.state;
    return (
      <div>
        <DayPilotScheduler
          {...config}
          ref={component => {
            this.scheduler = component && component.control;
          }}
        />
      </div>
    );
  }
}

export default Scheduler;

How to add resource groups?

react scheduler how to add resource group

The Scheduler includes a built-in UI feature for adding new rows. We will use it to provide an interface for adding new groups (top-level rows).

The first step is to enable the feature using rowCreateHandling property. This will display a special row at the bottom of the Scheduler. By default, it displays “New row…” text. We will change this to “New group…” using rowCreateText property.

As soon as the user enters a new group name in the field and confirms using <enter>, the Scheduler will fire onRowCreate event. We will use this event to add a new group to the Scheduler using rows.add() method.

This new row will be displayed as the last item at the top level.

this.state = {
  rowCreateHandling: "Enabled",
  rowCreateText: "New group...",
  onRowCreate: args => {

    const row = {
      name: args.text,
      id: DayPilot.guid()
    }
    this.scheduler.rows.add(row);

  },
  // ...
}

How to add child resources?

react scheduler how to add child resources

In order to let users add child resources to the groups, we will enable a row header context menu using contextMenuResource property.

The “Add resource…” item will open a modal dialog where the user can enter the new resource name.

When the modal dialog closes, we add the new resource as the last child of the selected group using rows.addChild() method of the Scheduler.

constructor(props) {
  super(props);

  this.state = {
    contextMenuResource: new DayPilot.Menu({
      onShow: args => {
        const row = args.source;
        const isParent = row.level === 0;
        args.menu.items[0].disabled = !isParent;
      },
      items: [
        {text: "Add resource....", onClick: args => this.add(args)}
      ]
    }),
    // ...
  };
}

async add(args) {

  const parent = args.source;
  const form = [
    {name: "Name", id: "name"}
  ];

  const data = {
    name: "Resource"
  };

  const modal = await DayPilot.Modal.form(form, data);
  if (modal.canceled) {
    return;
  }

  const row = modal.result;
  row.id = DayPilot.guid();

  this.scheduler.rows.addChild(parent, row);
}

How to edit Scheduler resources?

react scheduler how to edit resources

Now we can extend the context menu with additional items. The “Edit…” item will let users modify the resource name.

It opens a modal dialog that lets users edit the resource name. The Scheduler updates the resource data using rows.update() method.

constructor(props) {
  super(props);

  this.state = {
    contextMenuResource: new DayPilot.Menu({
      items: [
        {text: "Edit...", onClick: args => this.edit(args) },
        // ...
      ]
    })
  };
}

async edit(args) {

  const form = [
    {name: "Name", id: "name"}
  ];
  const row = args.source;
  const data = row.data;

  const modal = await DayPilot.Modal.form(form, data);

  if (modal.canceled) {
    return;
  }

  this.scheduler.rows.update(modal.result);
}

How to delete resources?

react scheduler how to delete resources

The “Delete…” menu item will let users delete a group or resource. Before the resource is actually deleted, we display a modal dialog with a confirmation dialog created using DayPilot.Modal.confirm() method.

constructor(props) {
  super(props);

  this.state = {
    contextMenuResource: new DayPilot.Menu({
      items: [
        {text: "Delete...", onClick: args => this.delete(args) },
        // ...
      ]
    })
  };
}

async delete(args) {
  const row = args.source;
  const modal = await DayPilot.Modal.confirm(`Do you really want to delete '${row.name}'?`);
  if (modal.canceled) {
    return;
  }

  this.scheduler.rows.remove(row);
}

How to add an icon to the row header?

react scheduler how to add icon to the row header

The resource context menu opens when the user right-clicks the row header. In order to make that option more prominent, we will add a special icon to the row header which will display on hover.

The icon can be added using an active area.

You can specify the active area position and dimensions (right, top, width, height properties), the SVG icon (symbol property), hover visibility (visibility property).

The action is set to "ContextMenu". This command will open the default context menu associated with the row header.

constructor(props) {
  super(props);

  this.state = {
    onBeforeRowHeaderRender: args => {
      args.row.areas = [
        {
          right: 6,
          top: 6,
          width: 18,
          height: 18,
          action: "ContextMenu",
          backColor: "#ffffff",
          symbol: "icons/daypilot.svg#minichevron-down-4",
          fontColor: "#cccccc",
          style: "border: 1px solid #ccc; cursor: pointer;",
          visibility: "Hover"
        }
      ]
    },
    // ...
  };
}

How to move resources using drag an drop?

react scheduler how to move resources drag drop

In order to enable drag and drop row moving, you need to set rowMoveHandling property to "Update".

When row moving is enabled, the React Scheduler will display a drag handle on hover. The users can drag the resource to a new location using this drag handle.

By default, the Scheduler lets users drag the rows to an arbitrary position in the tree. As we want to the resources and groups stay at the same level (only groups are allowed at the top level and groups can only contain resources, not other groups) we will enforce the same level using rowMoveSaveLevelOnly property.

this.state = {
  rowMoveHandling: "Update",
  onRowMove: args => {
    console.log("onRowMove", args);
  },
  rowMoveSameLevelOnly: true,
  // ...
}