Features

  • The application uses React monthly calendar component from DayPilot Pro for JavaScript
  • Calendar events area loaded using React automatic change detection
  • The calendar component appearance is customized using CSS
  • The calendar events use a context menu to provide additional options (plus a context menu 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 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. 

The daypilot-pro-react package is hosted at npm.daypilot.org - you can get an installation command for the latest version there (for NPM and Yarn):

react-package-at-npm-daypilot-org.png

We will create a new React component class (Month) that will wrap the DayPilot calendar component and specify its properties. Our Month component 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 in React

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: "2019-09-01",
      eventEndSpec: "Date",
    };
  }

  componentDidMount() {
    // load event data
    this.setState({
      events: [
        {
          id: 1,
          text: "Event 1",
          start: "2019-09-04",
          end: "2019-09-06"
        },
        {
          id: 2,
          text: "Event 2",
          start: "2019-09-04",
          end: "2019-09-05"
        },
        {
          id: 3,
          text: "Event 3",
          start: "2019-09-04",
          end: "2019-09-04"
        },
        {
          id: 4,
          text: "Event 4",
          start: "2019-09-11",
          end: "2019-09-11"
        },
        {
          id: 5,
          text: "Event 5",
          start: "2019-09-11",
          end: "2019-09-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-component-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: "2019-09-04",
          end: "2019-09-06"
        },
        {
          id: 2,
          text: "Event 2",
          start: "2019-09-04",
          end: "2019-09-05"
        },
        {
          id: 3,
          text: "Event 3",
          start: "2019-09-04",
          end: "2019-09-04"
        },
        {
          id: 4,
          text: "Event 4",
          start: "2019-09-11",
          end: "2019-09-11"
        },
        {
          id: 5,
          text: "Event 5",
          start: "2019-09-11",
          end: "2019-09-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;
    font-size: 12px;
    padding: 0px 10px;
    opacity: 0.8;
    border-radius: 5px;

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

React Monthly Calendar: Event Appearance

react-monthly-calendar-component-tutorial-event-appearance.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-component-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 3px; 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 3px; cursor:pointer;"}
        ];
        args.data.borderColor = "darker";
      },
      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 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;

src/month/MonthStyles.css

/* calendar event */
.month_default_event_inner {
    background: #999999;
    border-color: #777777;
    color: #fff;
    font-size: 12px;
    padding: 0px 10px;
    opacity: 0.8;
    border-radius: 4px;

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

/* context menu icons */
.icon:before {
    position: absolute;
    left: 0px;
    margin-left: 8px;
    margin-top: 3px;
    width: 14px;
    height: 14px;
    content: '';
}

.icon-blue:before { background-color: #3c78d8; }
.icon-green:before { background-color: #6aa84f; }
.icon-yellow:before { background-color: #e69138; }
.icon-red:before { background-color: #cc0000; }

History

  • September 23, 2019: Upgraded to DayPilot Pro for JavaScript 2019.3.4039, styling updates.
  • June 15, 2018: Initial release