Now.js Framework Documentation

Now.js Framework Documentation

EventCalendar

EN 06 May 2026 07:38

EventCalendar

EventCalendar is the calendar component used by Now.js applications for month, week, and day scheduling views. It is exposed as window.EventCalendar. When the Now.js core runtime is present, the bundle also registers the manager so it is available from Now.getManager('eventCalendar').

Files And Build

  • Runtime bundle: Now/dist/eventcalendar.min.css, Now/dist/eventcalendar.min.js
  • Source entry: Now/entry-eventcalendar.js
  • Source implementation: js/components/EventCalendar.js
  • Source styles: Now/css/event-calendar.css
  • Rebuild command: npm run build:eventcalendar

Features

  • Month, week, and day views
  • Auto locale resolution with framework i18n first, then Thai and English fallback strings
  • Two scheduling semantics: continuous and recurring-slot
  • Multi-day spanning bars in month and week views
  • Overlap column layout for timed events in week and day views
  • API loading with nested response extraction via eventDataPath
  • Event click API integration through ResponseHandler
  • Optional month period picker with min/max bounds
  • Keyboard navigation and framework event emission
  • Auto discovery on DOM ready and supported SPA lifecycle events

Loading The Bundle

EventCalendar is designed to run with the Now.js core runtime. Load the core CSS and JS before the EventCalendar bundle.

Requirements

  • Required for normal runtime: Now/dist/now.core.min.css, Now/dist/now.core.min.js
  • Required for framework event emission: EventManager from the core bundle
  • Optional but recommended: Modal or DialogManager for detail popups
  • Optional integrations: ApiService, ResponseHandler, and I18nManager

Load Order

<link rel="stylesheet" href="Now/dist/now.core.min.css">
<link rel="stylesheet" href="Now/dist/eventcalendar.min.css">

<script src="Now/dist/now.core.min.js"></script>
<script src="Now/dist/eventcalendar.min.js"></script>

EventCalendar.init() runs automatically after the script loads. Calendars are discovered on DOM ready and after route:changed, modal:shown, and page:loaded. Locale strings are refreshed again after locale:changed and i18n:updated.

If your application injects calendar markup outside those lifecycle events, call EventCalendar.discoverCalendars(container) after you insert the new DOM.

Basic Usage

Declarative HTML

<div
  id="calendar"
  data-event-calendar
  data-view="month"
  data-views="month,week,day"
  data-locale="en"
  data-first-day="1"
  data-max-events="3"
  data-show-period-picker="true"
  data-api="api/calendar"
  data-event-data-path="data"
></div>

What this example does:

  • data-view="month" starts in month view
  • data-first-day="1" uses Monday as the first day of the week
  • data-api loads the visible range from the server
  • data-event-data-path="data" extracts events from a { data: [...] } response

Expected result: the calendar renders the month view and requests the visible date range from api/calendar on first render.

JavaScript API

// Create or reuse the instance bound to #calendar.
const calendar = EventCalendar.create('#calendar', {
  defaultView: 'month',
  locale: 'en',
  showPeriodPicker: true,
  minDate: '2026-01',
  maxDate: '2026-12',
  onDateClick(date, instance) {
    console.log('Date clicked', date, instance);
  }
});

// Inject local events directly when you are not using config.api.
EventCalendar.setEvents(calendar, [
  {
    id: 'meeting-01',
    title: 'Team Meeting',
    start: '2026-04-21T09:00:00',
    end: '2026-04-21T10:30:00',
    allDay: false,
    scheduleType: 'continuous',
    color: '#4285F4'
  },
  {
    id: 'booking-01',
    title: 'Vehicle Reservation',
    start: '2026-04-21T08:00:00',
    end: '2026-04-23T17:00:00',
    allDay: false,
    scheduleType: 'continuous',
    color: '#EA4335'
  },
  {
    id: 'class-slot',
    title: 'Daily Class 08:00 - 09:00',
    start: '2026-04-21T08:00:00',
    end: '2026-04-27T09:00:00',
    startDate: '2026-04-21',
    endDate: '2026-04-27',
    slotStartTime: '08:00',
    slotEndTime: '09:00',
    scheduleType: 'recurring-slot',
    color: '#34A853'
  }
]);

What this example does:

  • create() returns the existing instance if #calendar was already initialized
  • setEvents() is the supported way to inject a local event array
  • The example mixes continuous and recurring-slot events in one calendar

Expected result: the calendar renders a month view with a timed single-day meeting, a spanning booking, and a recurring daily slot.

Advanced Examples

API-Backed Calendar With Nested Response Data

<script>
  window.App = {
    calendar: {
      handleEventClick(eventData, instance) {
        console.log('Clicked event', eventData, instance);
      }
    }
  };
</script>

<div
  id="api-calendar"
  data-event-calendar
  data-view="month"
  data-api="/api/calendar"
  data-event-data-path="data.items"
  data-on-event-click="App.calendar.handleEventClick"
></div>

What this example does:

  • Requests /api/calendar?start=YYYY-MM-DD&end=YYYY-MM-DD
  • Extracts the event array from response.data.items
  • Resolves the click callback from a global function path string

Recurring Daily Slot With Server-Driven Detail Actions

const bookingCalendar = EventCalendar.create('#booking-calendar', {
  defaultView: 'week',
  scheduleMode: 'continuous',
  onEventClickApi: '/api/bookings/{id}'
});

EventCalendar.setEvents(bookingCalendar, [
  {
    id: 'slot-01',
    title: 'Training Room 08:00 - 09:00',
    start: '2026-04-21T08:00:00',
    end: '2026-04-27T09:00:00',
    startDate: '2026-04-21',
    endDate: '2026-04-27',
    slotStartTime: '08:00',
    slotEndTime: '09:00',
    scheduleType: 'recurring-slot',
    color: '#34A853'
  }
]);

What this example does:

  • recurring-slot renders one timed occurrence for each day in the inclusive date range
  • onEventClickApi requests booking details when the event is clicked
  • If the response contains actions, ResponseHandler processes them before the built-in detail modal fallback is used

Configuration Options

Option Data attribute Default Description
defaultView data-view 'month' Initial view. Allowed values: 'month', 'week', 'day'.
views data-views ['month', 'week', 'day'] Enabled views. Declarative form is a comma-separated string.
locale data-locale 'auto' Locale preference. Uses the active i18n manager, the document language, or the browser locale when set to 'auto'.
timezone - 'local' Parsing mode for incoming dates. 'UTC' adjusts non-timezone strings to UTC semantics.
scheduleMode data-schedule-mode 'continuous' Fallback scheduling semantic when an event payload omits scheduleType.
firstDayOfWeek data-first-day 0 First weekday, where 0 is Sunday.
maxEventsPerDay data-max-events 3 Maximum inline month items and maximum visible spanning rows in standard layouts.
showNavigation data-show-navigation true Show previous, next, and Today controls.
showToday data-show-today true Show the Today button.
showViewSwitcher data-show-view-switcher true Show month, week, and day switch buttons.
showPeriodPicker data-show-period-picker false Show month and year dropdowns in month view only.
api data-api null Event endpoint. When set, initial load and navigation request the visible date range from the API.
apiMethod data-api-method 'GET' HTTP method used by the fetch fallback. When ApiService is available, the component uses ApiService.get(url).
eventDataPath data-event-data-path 'data' Property path used to extract the event array from API responses.
minDate data-min-date null Lower bound for navigation and picker options. Accepts Date, YYYY-MM, or YYYY-MM-DD.
maxDate data-max-date null Upper bound for navigation and picker options. Accepts Date, YYYY-MM, or YYYY-MM-DD.
yearRangeBefore data-year-range-before 10 Picker year range before the current year when no minDate is set.
yearRangeAfter data-year-range-after 10 Picker year range after the current year when no maxDate is set.
eventColors - built-in palette Fallback color palette used when an event payload omits color.
onDateClick data-on-date-click null Callback function or global function path string invoked after a date click.
onEventClick data-on-event-click null Callback function or global function path string invoked after an event click.
onEventClickApi data-on-event-click-api null URL template for click-to-API flows, for example api/events/{id}.

Event Payload

The component accepts plain event objects and normalizes them before rendering.

{
  "id": "event-001",
  "title": "Recurring Training",
  "start": "2026-04-21T08:00:00",
  "end": "2026-04-27T09:00:00",
  "allDay": false,
  "scheduleType": "recurring-slot",
  "startDate": "2026-04-21",
  "endDate": "2026-04-27",
  "slotStartTime": "08:00",
  "slotEndTime": "09:00",
  "color": "#34A853",
  "description": "Instructor-led class",
  "location": "Room A"
}
Field Type Notes
id string Optional. Auto-generated if omitted.
title string Optional, falls back to 'Untitled'.
start string|Date Required for useful rendering. Used for parsing, sorting, and default range values.
end string|Date Optional. Defaults to the parsed start date when omitted.
allDay boolean For continuous events this defaults to true unless you explicitly send false. For timed bookings, send allDay: false. For recurring-slot, the component forces allDay to false.
scheduleType / scheduleMode string Supported values are continuous and recurring-slot. scheduleType on the event overrides the calendar-level scheduleMode.
startDate / rangeStart string|Date Optional inclusive range start used mainly for recurring-slot.
endDate / rangeEnd string|Date Optional inclusive range end used mainly for recurring-slot.
slotStartTime string HH:mm time for recurring daily slots.
slotEndTime string HH:mm time for recurring daily slots.
color string Event accent color. Falls back to the configured palette.
description, location, category string Optional metadata used by modal and detail flows.

During normalization the original object is preserved internally as event.data. URL templates such as data-on-event-click-api="/api/bookings/{id}" can resolve placeholders from the normalized event object and, when nested lookup helpers are available, from the original payload as well.

Scheduling Semantics

continuous

  • Default behavior
  • Same-day timed events render as timed blocks when allDay: false is provided
  • Multi-day events render as spanning bars in month view and in the week all-day lane
  • In week and day views, a multi-day timed event renders timed slices only on the start day and end day instead of duplicating the block in every day column

recurring-slot

  • Represents the same daily time slot repeated across an inclusive date range
  • Never renders in the month or week all-day lane
  • Renders inline in month cells for each date in the range
  • Renders timed occurrences for every day in week and day views using slotStartTime and slotEndTime

Rendering Notes

  • Month view measures live DOM metrics to align spanning bars and inline items. The spacing is not based on a fixed hard-coded pixel formula.
  • Week and day timed events use overlap columns so concurrent items appear side-by-side instead of stacking on top of each other.
  • Under 480px width, month view keeps a compact single-row spanning-bar lane. Hidden spanning bars are added to the +N more indicator instead of disappearing.
  • recurring-slot items receive .ec-schedule-recurring-slot, which is styled with a dashed accent border by the default CSS.

API Response Handling

loadEvents() requests the visible date range using start and end query parameters in YYYY-MM-DD format.

The component can extract events from several response shapes:

  • Direct array response
  • response.data
  • response.data.data
  • response.data.data.data
  • The property path defined by eventDataPath

Example:

{
  "success": true,
  "data": [
    {
      "id": "booking-01",
      "title": "Vehicle Reservation",
      "start": "2026-04-21 08:00:00",
      "end": "2026-04-23 17:00:00",
      "allDay": false,
      "scheduleType": "continuous",
      "color": "#EA4335"
    }
  ]
}

Public JavaScript API

Method Parameters Returns Notes
init(options) global defaults object EventCalendar Merges global defaults, binds lifecycle handlers once, and scans the current DOM.
discoverCalendars(container) Element or document void Scans container for [data-event-calendar] nodes and creates instances for them.
create(element, options) selector or Element, optional config instance or null Reuses the existing instance if the element was already initialized.
addEvent(calendar, event) instance or selector, event object void Normalizes the new event, re-renders, and emits eventcalendar:eventAdd.
removeEvent(calendar, eventId) instance or selector, string id void Removes a matching event, re-renders, and emits eventcalendar:eventRemove.
updateEvent(calendar, eventId, partialData) instance or selector, string id, partial event object void Re-normalizes the merged event and emits eventcalendar:eventUpdate.
getEvents(calendar, start, end) instance or selector, optional Date range array When start and end are provided, returns events whose ranges overlap that window.
setEvents(calendar, events) instance or selector, event array void Replaces the local event collection and re-renders immediately.
refreshEvents(calendar) instance or selector void Reloads from config.api when an API endpoint is configured.
getInstance(element) selector, Element, or instance instance or null Returns the bound instance or null if the element is not initialized.
navigate(calendar, direction) instance or selector, -1 or 1 void Moves the current period backward or forward according to the active view.
goToToday(calendar) instance or selector void Jumps to the current date and emits eventcalendar:today.
changeView(calendar, view) instance or selector, 'month', 'week', or 'day' void Switches view and re-renders the current data. If your API returns only the current visible range, call refreshEvents() after switching to a wider view.
destroy(calendar) instance or selector void Removes listeners, clears the DOM, unregisters the instance, and emits eventcalendar:destroy.

Emitted Events

The component emits framework events through EventManager and also dispatches matching DOM CustomEvents on document.

Event detail payload
eventcalendar:dateClick { date, instance, element }
eventcalendar:eventClick { event, instance, element }
eventcalendar:navigate { date, direction, view, instance }
eventcalendar:today { date, view, instance }
eventcalendar:viewChange { view, date, instance }
eventcalendar:periodChange { date, year, month, view, instance }
eventcalendar:eventAdd { event, instance }
eventcalendar:eventRemove { eventId, instance }
eventcalendar:eventUpdate { event, instance }
eventcalendar:destroy { element }

Example listener:

document.addEventListener('eventcalendar:eventClick', (event) => {
  console.log('Clicked event payload', event.detail.event);
});

Accessibility And Interaction

  • The root calendar element uses role="application" and becomes focusable with tabindex="0" when needed.
  • The current period label uses role="status" and aria-live="polite" so screen readers announce date-range changes.
  • Navigation buttons and the Today button receive localized aria-label values.
  • Keyboard shortcuts are built in: ArrowLeft and ArrowRight navigate, Home or T go to today, and M, W, D switch to enabled month, week, and day views.

ResponseHandler Integration

When onEventClickApi is set, the event click URL is created by replacing placeholders with event data.

<div
  data-event-calendar
  data-api="api/calendar"
  data-on-event-click-api="api/calendar/{id}"
></div>

Current behavior:

  • If the API response contains actions, ResponseHandler processes them
  • If it does not contain actions, the component merges any returned payload.data into the clicked event and falls back to the built-in event detail modal
  • If onEventClick is also configured, the callback still runs after eventcalendar:eventClick is emitted

Gotchas And Best Practices

  • Send allDay: false for timed continuous events. If you omit it, the event is treated as an all-day item.
  • Use scheduleType: 'recurring-slot' only for daily repeating time slots across a date range. Use continuous for true spanning reservations.
  • Load Modal or DialogManager if you want the built-in detail popup. Otherwise provide your own onEventClick handler.
  • If you enable showPeriodPicker, also set minDate and maxDate when the allowed booking range is limited.
  • If your application injects HTML outside the supported SPA lifecycle events, call EventCalendar.discoverCalendars(container) yourself.
  • If you switch from a narrower API-loaded view to a wider view, call EventCalendar.refreshEvents(calendar) to request the wider range.
  • After editing source code or styles, rebuild the shipped products with npm run build:eventcalendar.

Styling Hooks

Useful CSS variables:

:root {
  --ec-primary: #4285F4;
  --ec-day-height: 120px;
  --ec-event-height: 22px;
  --ec-event-gap: 2px;
  --ec-allday-label-width: 60px;
  --ec-time-label-width: 60px;
}

Useful state classes:

  • .ec-schedule-continuous
  • .ec-schedule-recurring-slot
  • .ec-continuation-slice
  • .ec-event-start
  • .ec-event-end
  • .ec-today
  • .ec-other-month

Additional Resources