premium

Exploring the Calendar

The Ext JS calendar is a powerful new component that allows users to incorporate their date and event data to create stunning custom calendars. The calendar utilizes a minimalistic design philosophy so that it can be extended to cater to any requirements. This minimalistic ideology extends to the data level as well.

The calendar package has been developed to excel in both toolkits, modern and classic. This means that you can create a cross platform solution to display your calendar information on any medium.

For a full example of the calendar integrated with Google Calendars, see our examples page.

You can also find smaller, and more specific examples in our Kitchen Sink:

Using the Calendar Package

Your calendar package should be located within the packages folder that came with your Ext JS Premium purchase. This packages folder should be copied to the root packages directory of the ext folder you use to generate applications.

For instance, if you have generated your application using Sencha Cmd, you would move the calendar package from:

  1. {premiumLocation}/ext/packages/calendar

to:

  1. {yourApplication}/ext/packages/calendar

or:

  1. {yourWorkspace}/ext/packages/calendar

Once your package is in appropriately located, you’ll need to modify your app.json file. Open {appDir}/app.json and add “calendar” to the “requires” block. It should now look like similar to the following code snippet:

  1. "requires": [
  2. "calendar"
  3. ],

Do keep in mind that your requires block may contain other items depending on your default theme.

Your application should now be ready to use the calendar!

Note: The calendar package is only available in Ext Premium. For more information about Ext Premium and our other products, please check out the products page.

Calendar Without a Cmd Application

You can also utilize the calendar package without a Cmd built application. However, you may need to use Cmd to build the initial CSS/JS builds if they do not appear in your package’s build folder.

We generally try to include pre-built packages, but sometimes they slip through the cracks and are not available until the next release.

Building a package is quick and simple. The process produces a singular JS/CSS file for inclusion in your non-Cmd application. To build these files, issue the following command from your CLI:

  1. //target a specific theme
  2. sencha package build {themeName}

Classic themes will just be theme names (triton, neptune, etc).

Modern themes will be prepended with “modern-“ (modern-neptune).

or

  1. //build for all themes
  2. sencha package build

You should now have a build folder that contains the following built files:

  1. calendar/{toolkit}/calendar.js
  2. calendar/{toolkit}/{theme}/resources/calendar-all.css

You can now include these outputs by whatever method you are using.

Views

At the heart of it, the calendar package is a collection of views that digest and visualize data based on its timeframe (months, days, weeks, etc).

The most common form of the calendar is actually a composite of views, wrapped in panels (more about panels below).
This little snippet will create a full multi-view calendar that displays a compilation of the three most common calendar views (month, week, day).

Expand Code

JS Run

  1. Ext.create('Ext.calendar.panel.Panel', {
  2. renderTo: Ext.getBody(),
  3. width:700,
  4. height:700
  5. });

Now that you’ve seen a few of the views in action, let’s talk a little about each view in particular.

Weeks

Displays a series of weeks, one week per row. Days are arranged in a table like format, each cell represents a single day. There is no time axis for this view.

Week

The view range is calculated to show an entire week based on the value.

image alt text

Month

Displays a full month. The visible date range is calculated to show the entire month.

image alt text

Days

Displays a series of days, where each column represents a single day. A vertical time axis is included in this view.

Day

The same as the Days but restricted to a single day.

image alt text

Event Widgets

Each event visible in the view is a widget subclass. Similar to the data model, there is an API defined by Ext.calendar.EventBase and a default implementation in Ext.calendar.Event. The same event class is used across all views, however it is displayed in 3 modes (with matching sass mixins):

  • weekspan -> Used in view.Weeks for events that have a duration longer than 24 hours. Also used in view.Days for the all day view.

  • weekinline -> Used in view.Weeks for events that have a duration shorter than 24 hours.

  • day -> Used in the body of view.Days.

Timezones

The base view class has a timezone configuration that indicates the timezone to display events in. If one is not specified, it defaults to the current user TZ. This can affect how events are displayed, possibly even different days for the same event.

Compact mode

The base view class has a compact config. This boolean configures whether or not the view is in compact mode. It’s expected that this will be used as a platform config or a responsive config. Setting compact mode by itself does not modify how the view displays, however what it does do is apply the compactOptions config over the top of the current configuration options. These compactOptions are what is used to specify what compactness means for a particular view.

Promise based validation

UI events can require validation and the ability to veto them. For some events, a decision can’t be deferred (for example, starting a drag). However, things like dropping an event don’t require immediate processing to resolve. In fact, a remote source may need to be consulted to determine this. To do this in a consistent manner, promises are used to handle the possibly asynchronous nature of the validation. These validation events typically follow this pattern:

  1. listeners: {
  2. validatefoo: : function(view, context) {
  3. context.validate = context.validate.then(function() {
  4. return true;
  5. });
  6. }
  7. }

As we can see above there is a validate promise already on the context object. The user can chain onto the existing promise provided, or create a new one, however it is important that context.validate is overwritten by the time the event handler returns so the user promise can be consumed.

What’s Next

Now that we’ve discussed the underlying views, let’s talk about the things you’ll be working with most often. Panels!

Panels

While the views themselves are the power behind the calendar, you will likely not interact them directly. Each view is wrapped in a panel that provide a lot of additional controls.

Remember this example?

Expand Code

JS Run

  1. Ext.create('Ext.calendar.panel.Panel', {
  2. renderTo: Ext.getBody(),
  3. width:700,
  4. height:700
  5. });

Ext.calendar.panel.Panel is actually a composition panel that compiles three different panels into a single view. It also includes navigational controls, a title to show the active value, and a switcher control to navigate between the views. This is the view one would see for a calendar application and will probably be the most commonly consumed component by developers.

Let’s look at the view panels that make up the composition panel.

Month

Month utilizes Ext.calendar.panel.Month

Expand Code

JS Run

  1. Ext.create('Ext.calendar.panel.Month', {
  2. renderTo: Ext.getBody(),
  3. width:700,
  4. height:700
  5. });

Week

Week utilizes Ext.calendar.panel.Week

Expand Code

JS Run

  1. Ext.create('Ext.calendar.panel.Week', {
  2. renderTo: Ext.getBody(),
  3. width:700,
  4. height:700
  5. });

Day

Day utilizes Ext.calendar.panel.Day.

Expand Code

JS Run

  1. Ext.create('Ext.calendar.panel.Day', {
  2. renderTo: Ext.getBody(),
  3. width:700,
  4. height:700
  5. });

In addition to the above three panels, there are also panels for the other two views not found in the combined class.

Days

Days utilizes Ext.calendar.panel.Days.

Expand Code

JS Run

  1. Ext.create('Ext.calendar.panel.Days', {
  2. renderTo: Ext.getBody(),
  3. width:700,
  4. height:700
  5. });

Weeks

Weeks utilizes Ext.calendar.panel.Weeks.

Expand Code

JS Run

  1. Ext.create('Ext.calendar.panel.Weeks', {
  2. renderTo: Ext.getBody(),
  3. width:700,
  4. height:700
  5. });

By virtue of being panels, all of these classes gain panel abilities (docking, titles, etc) in addition to including a base header class that can display header information.

These view wrappers relay events and configurations directly onto the panel itself. This allows listeners, getters and setters to be called directly on the panel, without needing to do panel.getView().foo().

These panels don’t introduce any new functionality or modify the views in any way. They are just small convenience classes that introduce the headers.

Data

Data is an integral part of the calendar package. Since the calendar can display many different types of views, the data package brings some complexity.

Models

There are two data model types in the calendar package.

Calendar

The calendar model is essentially a group for events. Several calendars may be displayed on the same view. Typically, a calendar will hold events of a particular related type. The calendar has a method to return the store that will contain the events for that calendar.

Event

An event is an occurrence with a start date, end date, and duration. Each event must belong to one and only one calendar.

For each model type, there is a base (EventBase/CalendarBase) that defines the required interface to use these model types. They are declared as mixins, but have some abstract parts that must be implemented by the consuming class. The calendar package provides a default implementation of both model classes.

Getting and Setting Data

Throughout the calendar package, the get/set methods of a model are never called. Instead, all data is accessed/manipulated via getter and setter methods:

  1. record.setTitle(record.getTitle() + ‘!’); // Yes
  2. record.set(‘title’, record.get(‘title’) + ‘!’); // No

The calendar makes no assumptions about the structure of the data provided, only that it exists. This abstraction of data access ensures flexibility for a wide range of data structures to be used.

As an example of this concept, the default model implementation of Ext.calendar.model.Event has a startDate and endDate field on the model. The getDuration method is calculated based on these values. Another possible implementation could be to have a startDate and duration field on the model and have the getEndDate be a calculated quantity. The API makes no distinction here, as long as the EventBase interface is fulfilled.

Stores

There are 3 store types in the calendar package.

Calendars

The calendar store holds a list of Calendar. This small extension adds a method to return an EventSource for all calendars in this store (see below). This store type is the type of store bound to all views.

Events

The event store holds a list of Event for a particular Calendar. The store is typically created by calling the events method on a Calendar model. When used with a calendar view, this will happen automatically. The event store is intended to be filled for a particular date range, which should be governed by the appropriate view. This store type implements pre-fetching (and pruning) for a smoother experience when switching between dates in the view.

EventSource

The EventSource store holds an aggregated list of events for all calendars in a Calendars store, that are valid for a particular date range. This is a utility class used by the views as a single point of communication. The responsibility of this class is to control each Events store to keep a single flattened list of events to display. The content of this store is driven by the attached Events stores and the current date range.

Loading Data

Loading calendar data is divided between model types. A typical initial load of a calendar happens in 2 stages:

  1. Make a request to the server to retrieve all available calendars (wait for return). Here is an example of a sample response for a list of calendars:
  1. [{
  2. "calendarId": 1,
  3. title”: Personal
  4. }, {
  5. calendarId”: 2,
  6. title”: Work
  7. }]
  1. Make a request to the server for each calendar to retrieve events for the active date range. The date range and the calendarId are sent to the server. Here is an example of a sample response for calendar’s events:
  1. [{
  2. "id“: 1001,
  3. “calendarId“: 1,
  4. “endDate“: "2016-05-30T01:30:00.000Z",
  5. “startDate“: "2016-05-30T00:30:00.000Z",
  6. “title“: "Meet with Development"
  7. }, {
  8. “id“: 1002,
  9. “calendarId“: 1,
  10. “endDate“: "2016-05-31T03:00:00.000Z",
  11. “startDate“: "2016-05-31T01:00:00.000Z",
  12. “title“: “Review with Marketing"
  13. }]

Throughout the lifetime of the calendar component, a number of similar requests for events will likely be made for pre-fetching and loading data.

Misc Data Information

Date Formatting

All dates in the data package are to be specified as UTC.

All Day Events

An event that is “all day” must be TZ immune and end at midnight the following day. For example, if an event is all day on Jan 1st and Jan 2nd, then the start of the event is Jan 01 00:00:00 GMT and the end date is Jan 03 00:00:00 GMT.

Editable Calendars

Calendars have an editable flag. If this is set to false, the UI will not allow adding, editing or removing of events inside the calendar.

Forms

Because the forms depend on the model types, they follow much the same pattern. There is a minimal API declared for the forms, nothing specific about the fields. The form receives a record (blank record for creation) and is expected to populate the data. There is both an add and edit form. The default implementation treats these forms as the same.

Theming

Fashion theming follows the same pattern as other components found in the framework.

There is one slight addition to theming, because the system allows for specifying colors as part of the data model. As such, the color palette for basic event styling is handled as part of the javascript code. If a calendar does not have a color assigned, a default will be pulled from the pool of calendars and be claimed for that calendar id.

To specify custom colors, there are 3 items to override:

  1. Ext.define('MyPalette', {
  2. override: 'Ext.calendar.theme.Theme',
  3. colors: [
  4. 'rgb(44,151,222)',
  5. 'rgb(233,75,53)',
  6. 'rgb(30,206,109)',
  7. 'rgb(156,86,184)',
  8. 'rgb(60,214,220)',
  9. 'rgb(232,126,3)',
  10. 'rgb(0,189,156)'
  11. ],
  12. lightColor: 'rgb(238,238,238)',
  13. darkColor: 'rgb(34,34,34)'
  14. });