React Scheduler Component Tutorial | DayPilot Code
source link: https://code.daypilot.org/77607/react-scheduler-tutorial
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
What you will learn in this tutorial
How to add a React Scheduler component to your application
How to display monthly and weekly scheduler and switch between the views using zoom
How to load reservation data
How to change event/reservation color
How to enable drag and drop operations (moving, resizing)
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.
How to start using the React Scheduler immediately?
Use the online Scheduler configurator. The React Scheduler component is easy to configure and it can be added to your application in a few steps that are described below.
However, you can skip the setup phase and use the online Scheduler UI Builder application to configure the React Scheduler component and download a ready-to-run project.
How to install and configure the React Scheduler component
We assume that you already have some experience with React and that you have the environment ready (Node.js and NPM are required).
Create a new React project
The first step is to create a new React project using create-react-app
:
npx create-react-app reach-scheduler
Install the DayPilot Pro package
In order to use DayPilot Pro in your React project it's necessary to install the DayPilot Pro React module.
You can install it using npm:
npm install https://npm.daypilot.org/daypilot-pro-react/trial/2021.2.4990.tar.gz
Or using yarn:
yarn add https://npm.daypilot.org/daypilot-pro-react/trial/2021.2.4990.tar.gz
You can get the latest version at npm.daypilot.org.
Add the React Scheduler component
The daypilot-pro-react
module includes a DayPilotScheduler
class which is a React component that provides access to DayPilot Scheduler JavaScript component using React API.
You can add the Scheduler to your React application using <DayPilotScheduler>
tag:
import React, {Component} from 'react'; import {DayPilotScheduler} from "daypilot-pro-react"; class Scheduler extends Component { render() { return ( <DayPilotScheduler /> ); } } export default Scheduler;
This will display an empty Scheduler component with the default configuration - without any resources or events/reservation:
How to add rows to the Scheduler
In order to display any useful data, we need to define rows using resources property:
import React, {Component} from 'react'; import {DayPilotScheduler} from "daypilot-pro-react"; class Scheduler extends Component { render() { return ( <DayPilotScheduler 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"} ]} /> ); } } export default Scheduler;
The React Scheduler component supports all properties from the JavaScript Scheduler API. We will add a couple more options to customize the Scheduler appearance.
In the default configuration, the time header of the Scheduler displays today (one cell per hour). We will use startDate, days, scale and timeHeaders properties to customize :
import React, {Component} from 'react'; import {DayPilot, DayPilotScheduler} from "daypilot-pro-react"; class Scheduler extends Component { render() { return ( <DayPilotScheduler startDate = {DayPilot.Date.today().firstDayOfMonth()} days = {DayPilot.Date.today().daysInMonth()} scale = {"Day"} timeHeaders = {[ { groupBy: "Month"}, { groupBy: "Day", format: "d"} ]} 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"} ]} /> ); } } export default Scheduler;
Our React Scheduler component now displays the current month, one cell per day:
We can also adjust the grid size using cellWidth and eventHeight properties of the React scheduler component:
<DayPilotScheduler cellWidth = {50} eventHeight = {35} //... />
The cellWidth
property defines the Scheduler grid cell width in pixels.
The eventHeight
property specifies the height of event boxes. There are no events in our Scheduler yet but the height of the Scheduler rows is automatically adjusted to match the event height.
How to load Scheduler reservations
So far, we have defined the Scheduler properties inline by specifying the values directly:
import React, {Component} from 'react'; import {DayPilot, DayPilotScheduler} from "daypilot-pro-react"; import Zoom from "./Zoom"; class Scheduler extends Component { render() { return ( <DayPilotScheduler startDate = {DayPilot.Date.today().firstDayOfMonth()} days = {DayPilot.Date.today().daysInMonth()} scale = {"Day"} // ... /> ); } } export default Scheduler;
However, some properties may need to be changed dynamically so we will define them using state
instead:
import React, {Component} from 'react'; import {DayPilot, DayPilotScheduler} from "daypilot-pro-react"; import Zoom from "./Zoom"; class Scheduler extends Component { constructor(props) { super(props); this.state = { startDate: "2018-05-01", days: 31, scale: "Day", // ... }; } render() { return ( <DayPilotScheduler startDate = {this.state.startDate} days = {this.state.days} scale = {this.state.scale} // ... /> ); } } export default Scheduler;
This will allow us to update the Scheduler appearance and data by simply changing the right properties of the state object. The Scheduler component will detect the change and update itself.
The list of properties loaded from state can get long and the properties assignment code is repetitive. Fortunately, ES6 supports the destructuring assignment expression which will let us assign all properties at once:
import React, {Component} from 'react'; import {DayPilot, DayPilotScheduler} from "daypilot-pro-react"; import Zoom from "./Zoom"; class Scheduler extends Component { constructor(props) { super(props); this.state = { startDate: "2021-06-01", days: 31, scale: "Day", // ... }; } render() { const {...config} = this.state; return ( <DayPilotScheduler {...config} // ... /> ); } } export default Scheduler;
In order to load events, we will define an array with event records and use the reference for the events
property:
import React, {Component} from 'react'; import {DayPilot, DayPilotScheduler} from "daypilot-pro-react"; import Zoom from "./Zoom"; class Scheduler extends Component { constructor(props) { super(props); this.state = { startDate: "2021-10-01", days: 31, scale: "Day", eventHeight:30, cellWidth: 50, timeHeaders: [ { groupBy: "Month"}, { groupBy: "Day", format: "d"} ], cellWidthSpec: "Auto", 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: [ {id: 1, text: "Event 1", start: "2021-10-02T00:00:00", end: "2021-10-05T00:00:00", resource: "A" }, {id: 2, text: "Event 2", start: "2021-10-03T00:00:00", end: "2021-10-10T00:00:00", resource: "C", barColor: "#38761d", barBackColor: "#93c47d" }, {id: 3, text: "Event 3", start: "2021-10-02T00:00:00", end: "2021-10-08T00:00:00", resource: "D", barColor: "#f1c232", barBackColor: "#f1c232" }, {id: 4, text: "Event 3", start: "2021-10-02T00:00:00", end: "2021-10-08T00:00:00", resource: "E", barColor: "#cc0000", barBackColor: "#ea9999" } ] }; } render() { const {...config} = this.state; return ( <div> <DayPilotScheduler {...config} /> </div> ); } } export default Scheduler;
The items of the events array need to follow the structure defined for DayPilot.Event.data property.
How to switch views using React Scheduler zoom
Now that we have defined the Scheduler properties dynamically we can use the React automatic change detection mechanism to change the Scheduler appearance dynamically.
Let's create a simple Zoom component using radio buttons that will let users choose the desired zoom level of the Scheduler:
scheduler/Zoom.js
import React, {Component} from 'react'; class Zoom extends Component { constructor(props) { super(props); this.state = { level: "month" } } change(ev) { const newLevel = ev.target.value; this.setState({ level: newLevel }); if (this.props.onChange) { this.props.onChange({level: newLevel}) } } render() { return ( <span className="toolbar-item"> Zoom: <label><input type="radio" name="zoom" value="month" onChange={ev => this.change(ev)} checked={this.state.level === "month"} /> Month</label> <label><input type="radio" name="zoom" value="week" onChange={ev => this.change(ev)} checked={this.state.level === "week"} /> Week</label> </span> ); } } export default Zoom;
The Zoom component displays radio buttons group with two zoom level options:
Month
Whenever the user selects a new value the internal component state is updated and the parent component is notified using a custom onChange callback. The onChange
argument holds the newly selected zoom level:
args.level
("month"
|"week"
)
Now we can add our new Zoom component to our application. We will listen to zoom level changes by defining an onChange
event handler. Our event handler updates the required properties of the Scheduler state
object. The Scheduler component detects a state change and updates automatically.
import React, {Component} from 'react'; import {DayPilot, DayPilotScheduler} from "daypilot-pro-react"; import Zoom from "./Zoom"; class Scheduler extends Component { constructor(props) { super(props); this.state = { startDate: "2021-10-01", days: 31, scale: "Day", timeHeaders: [ { groupBy: "Month"}, { groupBy: "Day", format: "d"} ], // ... }; } zoomChange(args) { switch (args.level) { case "month": this.setState({ startDate: DayPilot.Date.today().firstDayOfMonth(), days: DayPilot.Date.today().daysInMonth(), scale: "Day" }); break; case "week": this.setState({ startDate: DayPilot.Date.today().firstDayOfWeek(), days: 7, scale: "Day" }); break; default: throw new Error("Invalid zoom level"); } } render() { const {...config} = this.state; return ( <div> <div className="toolbar"> <Zoom onChange={args => this.zoomChange(args)} /> </div> <DayPilotScheduler {...config} /> </div> ); } } export default Scheduler;
How to handle user events (drag and drop)
The Scheduler defines event handlers that you can use to get notifications of user changes. Let's see how it works for drag and drop event moving.
Simply define a callback method that receives an args
object as a parameter. The args
object holds details about the drag and drop action and you can use it to notify the server about the change:
<DayPilotScheduler // ... onEventMoved={args => { console.log("Event moved: ", args.e.data.id, args.newStart, args.newEnd, args.newResource); }} />
How to resize Scheduler events
You can handle drag and drop event resizing using the same mechanism. The Scheduler events/reservations can be resized by dragging either the start or end. As soon as the user finishes the resizing the Scheduler fires onEventResized
event.
The resizing is enabled by default (eventResizeHandling is set to "Update"
) and all we need to do is to add a new onEventResized event handler:
<DayPilotScheduler // ... onEventResized={args => { console.log("Event resized: ", args.e.data.id, args.newStart, args.newEnd); this.scheduler.message("Event resized: " + args.e.data.text); }} />
How to add new events/reservations
To add support for creating new events, we need to handle onTimeRangeSelected event handler:
<DayPilotScheduler // ... onTimeRangeSelected={args => { DayPilot.Modal.prompt("New event name", "Event").then(modal => { this.scheduler.clearSelection(); if (!modal.result) { return; } this.scheduler.events.add({ id: DayPilot.guid(), text: modal.result, start: args.start, end: args.end, resource: args.resource }); }); }} ref={component => { this.scheduler = component && component.control; }} />
The event handler will open a simple prompt using the built-in DayPilot.Modal dialog and ask for the event name. When the user hits OK, we create the event using the direct API - events.add() method.
How to display a message to the user using React Scheduler API
The Scheduler component offers a rich API which you can use in your application. The Scheduler methods let you use advanced features that can't be controlled by properties.
The following example uses message() method to display a custom message at the top of the Scheduler. It fades away after a couple of seconds and it can be used to provide feedback to users.
Before we can call the Scheduler methods, we need to get a reference to the DayPilot.Scheduler
object. It's available as control
property of the DayPilotScheduler
React component. We will store the reference as scheduler
property of our control using ref
attribute:
<DayPilotScheduler // ... ref={component => { this.scheduler = component && component.control; }} />
Now we can use this.scheduler
to invoke the Scheduler methods directly:
<DayPilotScheduler // ... onEventMoved={args => this.scheduler.message("Event moved: " + args.e.data.text)} // ... />
Can you change the color of reservations?
The React Scheduler component is very customizable and it lets you change the event color easily.
By default, the events are white and display a colored bar at the top. If you want to hide the bar, you need to add durationBarVisible
property to the config:
this.state = { durationBarVisible: false, // ... }
To change the event color depending on the event type, you need to use onBeforeEventRender
event handler. This event is fired for every event/reservation when it is loaded.
onBeforeEventRender: args => { if (!args.data.backColor) { args.data.backColor = "#93c47d"; } args.data.borderColor = "darker"; args.data.fontColor = "white"; }
The "darker"
value of the borderColor
property is a built-in helper that will calculate a darker color from backColor
value automatically.
Full Source Code of Scheduler.js
scheduler/Scheduler.js
import React, {Component} from 'react'; import {DayPilot, DayPilotScheduler} from "daypilot-pro-react"; import Zoom from "./Zoom"; class Scheduler extends Component { constructor(props) { super(props); this.state = { startDate: "2021-06-01", days: 31, scale: "Day", timeHeaders: [ {groupBy: "Month"}, {groupBy: "Day", format: "d"} ], cellWidthSpec: "Auto", cellWidth: 50, durationBarVisible: false, treeEnabled: true, rowHeaderColumns: [ {name: "Car"}, {name: "Seats", display: "seats", width: 50}, {name: "Doors", display: "doors", width: 50}, {name: "Transmission", display: "transmission", width: 90}, ], resources: [ { name: "Convertible", id: "G2", expanded: true, children: [ {name: "MINI Cooper", seats: 4, doors: 2, transmission: "Automatic", id: "A"}, {name: "BMW Z4", seats: 4, doors: 2, transmission: "Automatic", id: "B"}, {name: "Ford Mustang", seats: 4, doors: 2, transmission: "Automatic", id: "C"}, {name: "Mercedes-Benz SL", seats: 2, doors: 2, transmission: "Automatic", id: "D"}, ] }, { name: "SUV", id: "G1", expanded: true, children: [ {name: "BMW X1", seats: 5, doors: 4, transmission: "Automatic", id: "E"}, {name: "Jeep Wrangler", seats: 5, doors: 4, transmission: "Automatic", id: "F"}, {name: "Range Rover", seats: 5, doors: 4, transmission: "Automatic", id: "G"}, ] }, ], events: [ {id: 101, text: "Reservation 101", start: "2021-06-02T00:00:00", end: "2021-06-05T00:00:00", resource: "A"}, {id: 102, text: "Reservation 102", start: "2021-06-06T00:00:00", end: "2021-06-10T00:00:00", resource: "A"}, { id: 103, text: "Reservation 103", start: "2021-06-03T00:00:00", end: "2021-06-10T00:00:00", resource: "C", backColor: "#6fa8dc", locked: true }, { id: 104, text: "Reservation 104", start: "2021-06-02T00:00:00", end: "2021-06-08T00:00:00", resource: "E", backColor: "#f6b26b", plus: true }, { id: 105, text: "Reservation 105", start: "2021-06-03T00:00:00", end: "2021-06-09T00:00:00", resource: "G", }, { id: 106, text: "Reservation 106", start: "2021-06-02T00:00:00", end: "2021-06-07T00:00:00", resource: "B", } ], onBeforeEventRender: args => { if (!args.data.backColor) { args.data.backColor = "#93c47d"; } args.data.borderColor = "darker"; args.data.fontColor = "white"; args.data.areas = []; if (args.data.locked) { args.data.areas.push( { right: 4, top: 8, height: 18, width: 18, symbol: "icons/daypilot.svg#padlock", fontColor: "white" } ); } else if (args.data.plus) { args.data.areas.push( { right: 4, top: 8, height: 18, width: 18, symbol: "icons/daypilot.svg#plus-4", fontColor: "white" } ); } }, }; } zoomChange(args) { switch (args.level) { case "month": this.setState({ startDate: new DayPilot.Date("2021-06-01").firstDayOfMonth(), days: new DayPilot.Date("2021-06-01").daysInMonth(), scale: "Day" }); break; case "week": this.setState({ startDate: new DayPilot.Date("2021-06-01").firstDayOfWeek(), days: 7, scale: "Day" }); break; default: throw new Error("Invalid zoom level"); } } cellWidthChange(ev) { const checked = ev.target.checked; this.setState({ cellWidthSpec: checked ? "Auto" : "Fixed" }); } render() { const {...config} = this.state; return ( <div> <div className="toolbar"> <Zoom onChange={args => this.zoomChange(args)}/> <button onClick={ev => this.scheduler.message("Welcome!")}>Welcome!</button> <span className="toolbar-item"><label><input type="checkbox" checked={this.state.cellWidthSpec === "Auto"} onChange={ev => this.cellWidthChange(ev)}/> Auto width</label></span> </div> <DayPilotScheduler {...config} onEventMoved={args => { console.log("Event moved: ", args.e.data.id, args.newStart, args.newEnd, args.newResource); this.scheduler.message("Event moved: " + args.e.data.text); }} onEventResized={args => { console.log("Event resized: ", args.e.data.id, args.newStart, args.newEnd); this.scheduler.message("Event resized: " + args.e.data.text); }} onTimeRangeSelected={args => { DayPilot.Modal.prompt("New event name", "Event").then(modal => { this.scheduler.clearSelection(); if (!modal.result) { return; } this.scheduler.events.add({ id: DayPilot.guid(), text: modal.result, start: args.start, end: args.end, resource: args.resource }); }); }} ref={component => { this.scheduler = component && component.control; }} /> </div> ); } } export default Scheduler;
Main React Application Class
App.js
import React, {Component} from 'react'; import './App.css'; import Scheduler from "./scheduler/Scheduler"; class App extends Component { render() { return ( <Scheduler/> ); } } export default App;
History
June 21, 2021: Row columns added, how to change event color
May 20, 2021: Upgraded to React 17, DayPilot Pro for JavaScript 2021.2.4990
November 22, 2020: Upgraded to DayPilot Pro for JavaScript 2020.4.4766
September 11, 2019: Upgraded to DayPilot Pro for JavaScript 2019.3.4000, toolbar styling, event creating, event resizing.
May 21, 2018: Component interop (zoom component), custom event bar color
May 18, 2018: Initial release
Recommend
-
6
FeaturesJavaScript Scheduler with client-side undo/redo functionalityThe universal UndoService stores history of all changes and provides undo() and redo() functions
-
69
How to define your own Scheduler event types and customize the appearance. You can change the event type using a context menu or a modal dialog.
-
32
OverviewThe JavaScript Scheduler component allows operations on multiple events.Copy events from multiple Scheduler rows using Ctrl+drag...
-
22
FeaturesAngular Scheduler component with support for row filtering from DayPilot Pro for JavaScriptReal-time filtering by row nameAllow...
-
7
FeaturesZoom using HTML5 slider controlZoom using buttons: [+] and [-]Custom zoom levels with defined properties (scale, visible days, time headers...)Includes a trial version o...
-
13
OverviewAngular 12 Scheduler componentIncludes the required boilerplate and it ready to runUses a local data source for simplicityIncludes a trial version of
-
34
FeaturesThis project uses Angular Scheduler component that displays a timeline for multiple resourcesIf none of the predefined Scheduler timeline
-
87
FeaturesReact 17 application that builds event calendar UI using React Event Calendar component from DayPilot Pro package
-
13
FeaturesFind Angular Scheduler rows that match the specified rule and highlight them using the built-in row selection feature.
-
9
OverviewLearn how to use the React Scheduler to display milestones.By default, the milestones are displayed using green diamonds.You can change the milestone color, shape, and description.
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK