Features

  • The application uses React monthly calendar component from DayPilot Pro for JavaScript
  • Loading calendar events using automatic state change detection
  • Custom calendar styling using CSS
  • Calendar event context menu with hint icon
  • 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.

Monthly Calendar Configurator

react-monthly-calendar-configurator.png

This React project was created using Monthly Calendar UI Builder - an online configurator that lets you create a customized monthly calendar configuration and download a full React project.

Basic React Monthly Calendar Configuration

react-monthly-calendar-tutorial-component.png

You can add the monthly calendar component to you application using <DayPilotMonth> component which is included in daypilot-pro-react package.

We will create a new React component class (Month) that will wrap the DayPilot calendar component. It defines the calendar appearance and behavior using configuration properties and event handlers.

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

class Month extends Component {

  constructor(props) {
    super(props);
    this.state = {
      startDate: DayPilot.Date.today(),
      eventEndSpec: "Date"
    };
  }

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

export default Month;

Loading Calendar Events

react-monthly-calendar-tutorial-loading-events.png

You can load events in componentDidMount() method using events property:

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

class Month extends Component {

  constructor(props) {
    super(props);
    this.state = {
      startDate: DayPilot.Date.today(),
      eventEndSpec: "Date",
    };
  }

  componentDidMount() {
    // load event data
    this.setState({
      events: [
        {
          id: 1,
          text: "Event 1",
          start: "2018-06-04",
          end: "2018-06-06"
        },
        {
          id: 2,
          text: "Event 2",
          start: "2018-06-04",
          end: "2018-06-05"
        },
        {
          id: 3,
          text: "Event 3",
          start: "2018-06-04",
          end: "2018-06-04"
        },
        {
          id: 4,
          text: "Event 4",
          start: "2018-06-11",
          end: "2018-06-11"
        },
        {
          id: 5,
          text: "Event 5",
          start: "2018-06-11",
          end: "2018-06-11"
        },
      ]
    });
    
  }

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

export default Month;

The structure of the data items is described in DayPilot.Event.data documentation.

Monthly Calendar Styling

react-monthly-calendar-tutorial-styling.png

The calendar element dimensions can be modified using DayPilot.Month object properties. We will use the properties (eventHeight, headerHeight, cellMarginBottom, cellHeaderHeader, eventMarginBottom) to increase the size of selected calendar elements.

import React, {Component} from 'react';
import {DayPilot, DayPilotMonth} from "daypilot-pro-react";
import "./MonthStyles.css";

class Month extends Component {

  constructor(props) {
    super(props);
    this.state = {
      startDate: DayPilot.Date.today(),
      eventEndSpec: "Date",
      eventHeight: 30,
      headerHeight: 30,
      cellMarginBottom: 10,
      cellHeaderHeight: 20,
      eventMarginBottom: 5
    };
  }

  componentDidMount() {
    // load event data
    this.setState({
      events: [
        {
          id: 1,
          text: "Event 1",
          start: "2018-06-04",
          end: "2018-06-06"
        },
        {
          id: 2,
          text: "Event 2",
          start: "2018-06-04",
          end: "2018-06-05"
        },
        {
          id: 3,
          text: "Event 3",
          start: "2018-06-04",
          end: "2018-06-04"
        },
        {
          id: 4,
          text: "Event 4",
          start: "2018-06-11",
          end: "2018-06-11"
        },
        {
          id: 5,
          text: "Event 5",
          start: "2018-06-11",
          end: "2018-06-11"
        },
      ]
    });

  }

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

export default Month;

The monthly calendar appearance is controlled by CSS. The component uses a built-in theme (month_default). You can create your own CSS theme using the online theme designer or you can simply override selected styles of the built-in theme:

MonthStyles.css

/* calendar event */
.month_default_event_inner {
    background: #999999;
    color: #fff;
    border: 1px none;
    font-size: 10pt;
    padding: 0px 10px;
    opacity: 0.8;
    border-radius: 5px;

    /* vertical centering */
    display: flex;
    align-items: center;
}

.month_default_header_inner {
    display: flex;
    align-items: center;
    justify-content: center;
}

Custom Event Appearance

react-monthly-calendar-tutorial-event-color.png

You can customize the event appearance using data object properties. You can set the properties on the server side (add them to the JSON object after loading the data from a database). You can also add them on the client side using onBeforeEventRender to save bandwidth.

We will set event background color using backColor property of the event data object:

this.setState({
  startDate: DayPilot.Date.today(),
  events: [
    {
      id: 1,
      text: "Event 1",
      start: first.addDays(3),
      end: first.addDays(5),
      backColor: "#cc0000"
    },
    {
      id: 2,
      text: "Event 2",
      start: first.addDays(3),
      end: first.addDays(4),
      backColor: "#e69138",
    },
    {
      id: 3,
      text: "Event 3",
      start: first.addDays(3),
      end: first.addDays(3),
      backColor: "#6aa84f"
    },
    {
      id: 4,
      text: "Event 4",
      start: first.addDays(10),
      end: first.addDays(10),
      backColor: "#3c78d8"
    },
    {
      id: 5,
      text: "Event 5",
      start: first.addDays(10),
      end: first.addDays(10)
    },
  ]
});

Monthly Calendar Context Menu

react-monthly-calendar-tutorial-context-menu.png

We will define a context menu using contextMenu property of the calendar component. The context menu can be activated by right-clicking the events.

this.state = {
  // ...
  contextMenu: new DayPilot.Menu({
    items: [
      {
        text: "Delete",
        onClick: args => {
          var e = args.source;
          this.calendar.events.remove(e);
        }
      },
      {
        text: "-"
      },
      {
        text: "Blue",
        icon: "icon icon-blue",
        color: "#3c78d8",
        onClick: args => this.updateColor(args.source, args.item.color)
      },
      {
        text: "Green",
        icon: "icon icon-green",
        color: "#6aa84f",
        onClick: args => this.updateColor(args.source, args.item.color)
      },
      {
        text: "Yellow",
        icon: "icon icon-yellow",
        color: "#e69138",
        onClick: args => this.updateColor(args.source, args.item.color)
      },
      {
        text: "Red",
        icon: "icon icon-red",
        color: "#cc0000",
        onClick: args => this.updateColor(args.source, args.item.color)
      },            {
        text: "Auto",
        color: "auto",
        onClick: args => this.updateColor(args.source, args.item.color)
      },

    ]
  }),
}

To provide a visual hint to the user, we will also add an active area that will activate the context menu on click:

this.state = {
  // ...
  onBeforeEventRender: args => {
    args.data.areas = [
      { top: 6, right: 10, width: 12, height: 14, icon: "icon-triangle-down", visibility: "Visible", action: "ContextMenu", style: "font-size: 12px; background-color: #fff; border: 1px solid #ccc; border-radius: 5px; padding: 3px 3px 0px 2px; cursor:pointer;"}
    ];
  },
  // ...
}

Full Source Code

src/month/Month.js

import React, {Component} from 'react';
import {DayPilot, DayPilotMonth} from "daypilot-pro-react";
import "./MonthStyles.css";
import "./icons/style.css";

class Month extends Component {

  constructor(props) {
    super(props);
    this.state = {
      eventHeight: 30,
      headerHeight: 30,
      cellMarginBottom: 10,
      cellHeaderHeight: 20,
      eventMarginBottom: 5,
      eventEndSpec: "Date",
      onBeforeEventRender: args => {
        args.data.areas = [
          { top: 6, right: 10, width: 12, height: 14, icon: "icon-triangle-down", visibility: "Visible", action: "ContextMenu", style: "font-size: 12px; background-color: #fff; border: 1px solid #ccc; border-radius: 5px; padding: 3px 3px 0px 2px; cursor:pointer;"}
        ];
      },
      contextMenu: new DayPilot.Menu({
        items: [
          {
            text: "Delete",
            onClick: args => {
              var e = args.source;
              this.calendar.events.remove(e);
            }
          },
          {
            text: "-"
          },
          {
            text: "Blue",
            icon: "icon icon-blue",
            color: "#3c78d8",
            onClick: args => this.updateColor(args.source, args.item.color)
          },
          {
            text: "Green",
            icon: "icon icon-green",
            color: "#6aa84f",
            onClick: args => this.updateColor(args.source, args.item.color)
          },
          {
            text: "Yellow",
            icon: "icon icon-yellow",
            color: "#e69138",
            onClick: args => this.updateColor(args.source, args.item.color)
          },
          {
            text: "Red",
            icon: "icon icon-red",
            color: "#cc0000",
            onClick: args => this.updateColor(args.source, args.item.color)
          },            {
            text: "Auto",
            color: "auto",
            onClick: args => this.updateColor(args.source, args.item.color)
          },

        ]
      }),
      onTimeRangeSelected: args => {
        let dp = this.calendar;
        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(),
            text: modal.result
          }));
        });
      },
    };
  }

  componentDidMount() {

    let first = DayPilot.Date.today().firstDayOfMonth();

    // load resource and event data
    this.setState({
      startDate: DayPilot.Date.today(),
      events: [
        {
          id: 1,
          text: "Event 1",
          start: first.addDays(3),
          end: first.addDays(5),
          backColor: "#cc0000"
        },
        {
          id: 2,
          text: "Event 2",
          start: first.addDays(3),
          end: first.addDays(4),
          backColor: "#e69138",
        },
        {
          id: 3,
          text: "Event 3",
          start: first.addDays(3),
          end: first.addDays(3),
          backColor: "#6aa84f"
        },
        {
          id: 4,
          text: "Event 4",
          start: first.addDays(10),
          end: first.addDays(10),
          backColor: "#3c78d8"
        },
        {
          id: 5,
          text: "Event 5",
          start: first.addDays(10),
          end: first.addDays(10)
        },
      ]
    });

  }

  updateColor(e, color) {
    e.data.backColor = color;
    this.calendar.events.update(e);
  }

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

export default Month;