ra-calendar
Display and manipulate events, drag and resize appointments, and browse a calendar in react-admin apps. This module integrates Full Calendar with react-admin, and supports:
- month, week, day views
- list view
- drag and resize events
- whole-day events
- creating an event by clicking in the calendar
- edition of event title, and metadata
- events spanning on multiple days
- recurring events
- background events
- theming
- locales and timezones
- resource time grid (e.g. rooms) (requires additional licence from Full Calendar)
Events Format
The calendar can display a list of Events. Events must be resources with at least a string id
, a title
, and a start
date. Here is a typical event:
{
id: '432646',
title: 'Package delivery',
start: '2020-10-20T08:00:00.000Z',
end: '2020-10-20T09:30:00.000Z',
},
Events can have many more fields, e.g. for recurrent events, groups, colors, etc. Check the Event format on the Full Calendar documentation.
That means that in order to be able to use ra-calendar
, your dataProvider
must return event-like objects for at least one resource.
In addition, the calendar queries a list of events in a time interval. Your dataProvider must support getList()
queries with an interval filter. By default, the interval filter looks like the following:
{
// lower time boundary (gte stands for 'greater than or equal')
start_gte: '2020-10-01T00:00:00.000Z',
// upper time boundary (lte stands for 'less than or equal')
start_lte: '2020-12-31T23:59:59.000Z'
}
The ra-calendar
provides a function to transform the interval into a filter (explained below).
<Calendar>
A wrapper around full-calendar's <FullCalendar>
component, using the react-admin Redux store as content provider, and linking to the edit and create views of the current resource. Must be used inside a <ListContext>
.
Use this component as a child of <List>
, as follows:
import { Calendar, getFilterValuesFromInterval } from '@react-admin/ra-calendar';
import { Admin, Resource, List, Edit, SimpleForm, TextInput, DateTimeInput } from 'react-admin';
const EventList: FC<ComponentProps<typeof List>> = props => (
<List
{...props}
filterDefaultValues={getFilterValuesFromInterval()}
perPage={1000}
pagination={false}
>
<Calendar />
</List>
);
const EventEdit: FC<ComponentProps<typeof Edit>> = props => (
<Edit {...props}>
<SimpleForm>
<TextInput source="title" />
<DateTimeInput source="start" />
<DateTimeInput source="end" />
</SimpleForm>
</Edit>
);
const EventCreate: FC<ComponentProps<typeof Create>> = props => (
<Create {...props}>
<SimpleForm>
<TextInput source="title" />
<DateTimeInput source="start" />
<DateTimeInput source="end" />
</SimpleForm>
</Create>
);
export const App: FC = () => (
<Admin dataProvider={dataProvider}>
<Resource
name="events"
list={EventList}
edit={EventEdit}
create={EventCreate}
/>
</Admin>
);
The getFilterValuesFromInterval()
function returns filter values based on the interval displayed on the screen (e.g. the current mont, the current week, etc). ra-calendar
does its best to minimize queries to the dataProvider
by requesting a 3 months interval by default (1 month before the current day, and 2 months after). You can change that behavior, and transform the filter
object sent to the dataProvider.getList()
method, by passing your own getFilterValueFromInterval
prop:
import { DatesSetArg } from '@fullcalendar/react';
import { add, sub, set } from 'date-fns';
/**
* By default, return an interval of 3 months around now (1 month before, 2 months after)
* unless the user requires a larger interval.
*
* This minimizes queries while navigating.
*/
const customGetFilterValues = (
dateInfo?: DatesSetArg,
filterValues: any = {}
): any => {
const now = set(new Date(), {
hours: 0,
minutes: 0,
seconds: 0,
milliseconds: 0,
});
const nowMinus1Month = sub(now, { months: 1 });
const nowPlus2Months = add(now, { months: 2 });
return !dateInfo ||
(dateInfo.start > nowMinus1Month && dateInfo.end < nowPlus2Months)
? {
...filterValues,
start_gte: nowMinus1Month.toISOString(),
start_lte: nowPlus2Months.toISOString(),
}
: {
...filterValues,
start_gte: dateInfo.startStr,
start_lte: dateInfo.endStr,
};
};
const EventList: FC<ComponentProps<typeof List>> = props => (
<List
{...props}
filterDefaultValues={customGetFilterValues()}
perPage={1000}
pagination={false}
>
<Calendar getFilterValuesFromInterval={customGetFilterValues} />
</List>
);
import { add, sub, set } from "date-fns";
/**
* By default, return an interval of 3 months around now (1 month before, 2 months after)
* unless the user requires a larger interval.
*
* This minimizes queries while navigating.
*/
const customGetFilterValues = (dateInfo, filterValues = {}) => {
const now = set(new Date(), {
hours: 0,
minutes: 0,
seconds: 0,
milliseconds: 0,
});
const nowMinus1Month = sub(now, { months: 1 });
const nowPlus2Months = add(now, { months: 2 });
return !dateInfo || (dateInfo.start > nowMinus1Month && dateInfo.end < nowPlus2Months)
? {
...filterValues,
start_gte: nowMinus1Month.toISOString(),
start_lte: nowPlus2Months.toISOString(),
}
: {
...filterValues,
start_gte: dateInfo.startStr,
start_lte: dateInfo.endStr,
};
};
const EventList = (props) => (
<List {...props} filterDefaultValues={customGetFilterValues()} perPage={1000} pagination={false}>
<Calendar getFilterValuesFromInterval={customGetFilterValues} />
</List>
);
CHANGELOG
v0.0.1
2020-10-21
- First release