Overview

Learn how to use the Navigator date picker component from the open-source DayPilot Lite for JavaScript scheduling library to highlight full or partial occupancy for each day.

Each day will show one of the following states:

  • Free for the whole day

  • Fully occupied

  • Only partially occupied (visually indicated using a diagonal split)

We’ll do this by assigning a CSS class to each day in onBeforeCellRender and then drawing a diagonal overlay using clip-path.

See also how to configue the diagonal cell split for the Scheduler component (for multiple resources):

Initializing the Date Picker (Navigator Component)

First, we create the DayPilot.Navigator component and configure handling of the reservation end date:

import {DayPilot} from "@daypilot/daypilot-lite-javascript";

const dp = new DayPilot.Navigator("dp", {
  eventEndSpec: "Date",
});

dp.init();

The eventEndSpec: "Date" config option makes sure the event start/end values match the Navigator’s per-day cells correctly (end is treated as a day boundary, not a time).

Detecting Partial / Full Occupancy per Day

Now we will customize the date picker cells using the onBeforeCellRender event handler.

onBeforeCellRender: (args) => {
  const list = args.cell.events.all();
  const day = args.cell.day;

  const startHere = list.some(e => e.start() === day);
  const endHere = list.some(e => e.end() === day);
  const full = list.some(e => e.start() !== day && e.end() !== day);

  // ...

}

We check the events that itersect the current day, returned by the args.cell.events.all() method:

  • startHere is true if at least one event starts on this day.

  • endHere is true if at least one event ends on this day.

  • full is true if an event covers the day completely (the day lies between the start and end).
    Since list already contains only events overlapping this day, the condition e.start() !== day && e.end() !== day effectively says: “this day is neither the start nor the end → it must be fully occupied.”

The free/busy highlighting is applied using a CSS class based on the calculated cell status:

args.cell.cssClass =
  full || (startHere && endHere) ? "full" :
    startHere ? "start" :
      endHere ? "end" :
        null;

The following CSS classes can be applied:

  • Cells with the full CSS class are fully occupied — either by a multi-day reservation or by two separate reservations that start and end on this day.

  • Cells with a single reservation starting on this day are given the start CSS class.

  • Cells with a single reservation ending on this day are given the end CSS class.

Drawing the Diagonal Overlay with CSS

We use a pseudo-element on the default Navigator cell:

<style>
  .navigator_default_cell::before {
    content: "";
    position: absolute;
    inset: 0;
    background: #93c47d;
    pointer-events: none;
    display: none;
  }

  .navigator_default_cell.start::before {
    display: block;
    clip-path: polygon(0 100%, 100% 0, 100% 100%);
  }

  .navigator_default_cell.end::before {
    display: block;
    clip-path: polygon(0 0, 100% 0, 0 100%);
  }

  .navigator_default_cell.full::before {
    display: block;
    clip-path: none;
  }
</style>

These CSS classes define a full rectangle overlay for the full class, and triangles for the start and end classes.

The triangles are created by clipping the pseudo-element content (clip-path).

The overlays use #93c47d as the background color - you can modify it as needed.

Loading the Reservation Data

Our app defines two sample reservations and loads them using the update() method:

const app = {
  init() {
    const events = [
      {
        start: DayPilot.Date.today().firstDayOfWeek().addDays(1),
        end: DayPilot.Date.today().firstDayOfWeek().addDays(2),
        resource: "A",
        id: 1,
        text: "Reservation 1"
      },
      {
        start: DayPilot.Date.today().firstDayOfWeek().addDays(4),
        end: DayPilot.Date.today().firstDayOfWeek().addDays(11),
        resource: "A",
        id: 2,
        text: "Reservation 2"
      },
    ];
    dp.update({events});
  }
};

app.init();