---
title: "Vue Event calendar Day Cell Summary View Example | Mobiscroll"
component: "Event calendar"
framework: "Vue"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/vue/eventcalendar/day-cell-aggregate-information"
---

# Day Cell Summary View - Vue Event calendar demo

Display color-coded daily event summaries in a Vue event calendar — each cell shows meetings and workload density, with drill-down to day and week views. For Vue and Nuxt.

- [Live demo](https://demo.mobiscroll.com/vue/eventcalendar/day-cell-aggregate-information)
- [View SFC JS code on GitHub](https://github.com/acidb/mobiscroll-demos-vue/tree/main/src/demos/eventcalendar/calendar-view/day-cell-aggregate-information)
- [View SFC TS code on GitHub](https://github.com/acidb/mobiscroll-demos-vue-ts/tree/main/src/demos/eventcalendar/calendar-view/day-cell-aggregate-information)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/eventcalendar/day-cell-aggregate-information.md), [jQuery](https://demo.mobiscroll.com/jquery/eventcalendar/day-cell-aggregate-information.md), [Angular](https://demo.mobiscroll.com/angular/eventcalendar/day-cell-aggregate-information.md), [React](https://demo.mobiscroll.com/react/eventcalendar/day-cell-aggregate-information.md)

## Description

This demo showcases an advanced event calendar implementation with extensive day cell customization using the [`calendarDay`](https://mobiscroll.com/docs/vue/eventcalendar/api#slot-calendarDay) , which completely overrides the default cell rendering.

The calendar provides a comprehensive visual overview of scheduling density and workload management through custom aggregate data visualization with stress-level indicators, weather information, and color-coded backgrounds.

This approach prevents the calendar from rendering individual event labels, allowing full control over the cell content. Each day cell displays aggregate information calculated from the actual events, received by the custom template.

Users can navigate smoothly from the monthly or weekly overview to a detailed daily schedule by clicking on a day cell, which highlights the selected day and loads all events. A convenient back button allows a quick return to the previous view. Each cell also includes an add button for creating new appointments, and events can be added by dragging or clicking in the week or day view.

## What this demo shows

- A full-month Eventcalendar with fully customized day cells that show daily summary content instead of individual event labels.
- **Day cell content** Each day cell shows the short day name, date, short month name, an optional status emoji when at least one meeting is scheduled, a weather icon with temperature, and separate counts for internal and client meetings.
- **Color coding** Days with no events use a white background, days with 1-2 events use green, days with 3-4 events use orange, and days with more than 4 events use red background.
- **Hover behavior** Hovering a day cell highlights it.
- **Add action** Each day cell includes a circular (+) add button in the lower-right corner for creating a meeting on that date.
- **Day view drill-down** Clicking a day cell opens a daily scheduler for the selected date.
- **Daily scheduling** In the daily scheduler, users can create time-specific events by double-clicking or clicking and dragging on the time grid.
- **Custom day header** The daily scheduler header repeats the daily summary information from the month view and includes a centered Back to calendar button with an X icon that returns to the month view.
- **Header layout** The Eventcalendar header shows the current month and year on the left, a segmented Calendar or Week view switcher in the center, and previous, Today, and next navigation controls on the right.
- **Week view** Switching to Week view opens a weekly scheduler for the current week, with a weekly calendar in the header and a weekly scheduler below it.
- **Weekly scheduling** In the weekly scheduler, users can create time-specific events by double-clicking or clicking and dragging on the time grid.
- **Weekly summaries** The weekly calendar header uses the same customized daily summary cells as the month view.

## Best for

- **Workload overview** Reviewing scheduling density across the month with color-coded daily summaries instead of individual event labels.
- **Meeting-heavy planning** Tracking internal and client meeting counts side by side for each day.
- **Operational context** Combining schedule volume with supporting signals like weather and status indicators across monthly, daily scheduler, and weekly scheduler views.
- **Progressive drill-down** Starting from a high-level monthly overview and opening day or week scheduling views when time-specific event management is needed.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-themeVariant) - option
- [`min`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-min) - option
- [`max`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-max) - option
- [`clickToCreate`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-dragToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-dragToResize) - option
- [`eventDelete`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-eventDelete) - option
- [`cssClass`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-cssClass) - option
- [`view`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-view) - option
- [`data`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-data) - option
- [`extendDefaultEvent`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-extendDefaultEvent) - option
- [`selectedDate`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-selectedDate) - option
- [`selected-date-change`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onSelectedDateChange) - event
- [`cell-click`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onCellClick) - event
- [`cssClass`](https://mobiscroll.com/docs/vue/forms/button#opt-cssClass) - option
- [`cssClass`](https://mobiscroll.com/docs/vue/forms/segmented#opt-cssClass) - option
- [`close`](https://mobiscroll.com/docs/vue/notifications/toast#event-onClose) - event

## Code

### SFC JS

```vue
<script setup>
import {
  formatDate,
  MbscButton,
  MbscCalendarNav,
  MbscCalendarNext,
  MbscCalendarPrev,
  MbscCalendarToday,
  MbscEventcalendar,
  MbscSegmented,
  MbscSegmentedGroup,
  MbscToast,
  setOptions
} from '@mobiscroll/vue'
import { ref } from 'vue'

setOptions({
  theme: 'ios',
  themeVariant: 'light'
})

const myCssClass = ref('mds-cell-template mds-cell-template-month-view')
const weatherCache = ref({})
const selectedView = ref('month')
const previousView = ref('month')
const currentDate = ref(new Date())
const isToastOpen = ref(false)
const toastMessage = ref('')

const myEvents = ref([
  {
    start: '2026-10-23T15:00',
    end: '2026-10-23T17:00',
    title: 'Operations Huddle',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-23T12:00',
    end: '2026-10-23T13:00',
    title: 'HR Policy Update',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-24T08:00',
    end: '2026-10-24T10:00',
    title: 'Team Retrospective',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-24T10:00',
    end: '2026-10-24T11:30',
    title: 'Proposal Review Meeting',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-24T12:00',
    end: '2026-10-24T13:00',
    title: 'Solutions Presentation',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-24T14:00',
    end: '2026-10-24T15:00',
    title: 'Follow-up Discussion',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-24T15:00',
    end: '2026-10-24T16:00',
    title: 'Performance Review',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-25T10:00',
    end: '2026-10-25T13:00',
    title: 'Client Onboarding',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-25T15:00',
    end: '2026-10-25T16:00',
    title: 'Marketing Campaign Brainstorm',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-02T10:00',
    end: '2026-10-02T12:00',
    title: 'Innovation Brainstorm',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-02T13:00',
    end: '2026-10-02T15:30',
    title: 'Onboarding Session',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-02T16:00',
    end: '2026-10-02T17:00',
    title: 'Discovery Call',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-09T09:00',
    end: '2026-10-09T10:00',
    title: 'Partnership Exploration',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-09T11:00',
    end: '2026-10-09T13:00',
    title: 'Service Implementation',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-09T14:00',
    end: '2026-10-09T15:00',
    title: 'Future Planning Summit',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-15T10:00',
    end: '2026-10-15T12:00',
    title: 'Strategy Alignment',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-18T12:00',
    end: '2026-10-18T14:00',
    title: 'Project Kick-off',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-18T15:00',
    end: '2026-10-18T16:00',
    title: 'Account Review',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-22T12:00',
    end: '2026-10-22T14:00',
    title: 'Deep Dive Session',
    type: 'meeting',
    color: '#634b67'
  },
  {
    title: 'Fresh start meeting',
    start: '09:00',
    end: '10:00',
    type: 'meeting',
    color: '#634b67',
    recurring: {
      repeat: 'weekly',
      weekDays: 'MO'
    }
  },
  {
    title: 'Weekly wrapup',
    start: '16:00',
    end: '16:30',
    type: 'meeting',
    color: '#634b67',
    recurring: {
      repeat: 'weekly',
      weekDays: 'FR'
    }
  }
])

const myView = ref({
  calendar: {
    type: 'month'
  }
})

function myDefaultEvent() {
  return {
    title: 'New appointment',
    type: 'appointment',
    color: '#656d49'
  }
}

function getWeatherForDate(date) {
  const key = date.getTime()
  if (!weatherCache.value[key]) {
    weatherCache.value[key] = generateRandomWeather(date)
  }
  return weatherCache.value[key]
}

function generateRandomWeather(date) {
  const weatherTypes = [
    { emoji: '☀️', min: 24, max: 30 },
    { emoji: '🌤️', min: 20, max: 25 },
    { emoji: '☁️', min: 17, max: 22 },
    { emoji: '🌧️', min: 15, max: 20 }
  ]
  const type = weatherTypes[Math.floor(Math.random() * weatherTypes.length)]
  const degree = Math.floor(Math.random() * (type.max - type.min + 1)) + type.min

  return {
    date: date,
    degree: degree,
    emoji: type.emoji
  }
}

function getStressLevel(nrEvents) {
  let emoji
  let color

  if (nrEvents < 1) {
    emoji = ''
    color = ''
  } else if (nrEvents < 3) {
    emoji = '😃'
    color = '#6ece86ff'
  } else if (nrEvents < 5) {
    emoji = '😐'
    color = '#d89c6aff'
  } else {
    emoji = '😫'
    color = '#d6727aff'
  }
  return { emoji: emoji, color: color }
}

function getNrEvents(events) {
  let nrMeetings = 0
  let nrAppointments = 0

  for (const event of events) {
    if (event['type'] === 'meeting') {
      nrMeetings++
    } else {
      nrAppointments++
    }
  }

  return { meetings: nrMeetings, appointments: nrAppointments }
}

function setSelectedView(view, date = undefined) {
  if (view === 'day') {
    previousView.value = selectedView.value
  }

  selectedView.value = view

  switch (view) {
    case 'month':
      myCssClass.value = 'mds-cell-template mds-cell-template-month-view'
      myView.value = {
        calendar: { type: 'month' }
      }
      break
    case 'week':
      myCssClass.value = 'mds-cell-template mds-cell-template-week-view'
      myView.value = {
        scheduler: {
          type: 'week',
          allDay: false,
          startTime: '08:00',
          endTime: '17:00'
        }
      }
      break
    case 'day':
      myCssClass.value = 'mds-cell-template mds-cell-template-day-view'
      myView.value = {
        scheduler: {
          type: 'day',
          allDay: false,
          startTime: '08:00',
          endTime: '17:00'
        }
      }
      break
  }

  if (date) {
    currentDate.value = date
  }
}

function handleCellClick(args) {
  const date = args.date
  const target = args.domEvent.target

  if (target.closest('.mds-cell-template-add')) {
    const year = date.getFullYear()
    const month = date.getMonth()
    const day = date.getDate()

    const newEvent = {
      title: 'New appointment',
      start: new Date(year, month, day, 9),
      end: new Date(year, month, day, 10),
      color: '#ecee8d'
    }

    myEvents.value = [...myEvents.value, newEvent]

    toastMessage.value = 'Appointment added to ' + formatDate('DDD D, MMM', date)
    isToastOpen.value = true
  } else if (selectedView.value === 'month') {
    setSelectedView('day', date)
  }
}

function getDayTemplate(args) {
  const date = args.date
  const nrEvents = getNrEvents(args.events)
  const nrAllEvents = args.events.length
  const stressLevel = getStressLevel(nrAllEvents)
  const weather = getWeatherForDate(date)

  return {
    dayContent: formatDate('DDD D, MMM', date) + ' ' + stressLevel.emoji,
    weather: weather.emoji + ' ' + weather.degree + '°C',
    stressLevelColor: stressLevel.color,
    nrMeetings: nrEvents.meetings,
    nrAppointments: nrEvents.appointments
  }
}

function handleSelectedDateChange(args) {
  currentDate.value = args.date
}
</script>

<template>
  <MbscEventcalendar
    :clickToCreate="true"
    :dragToCreate="true"
    :dragToMove="true"
    :dragToResize="true"
    :eventDelete="true"
    :cssClass="myCssClass"
    :view="myView"
    :data="myEvents"
    :extendDefaultEvent="myDefaultEvent"
    :selectedDate="currentDate"
    @selected-date-change="handleSelectedDateChange"
    @cell-click="handleCellClick"
  >
    <template #calendarDay="data">
      {{ void (dayData = getDayTemplate(data)) }}
      <div
        class="mds-cell-template-cont"
        :style="{
          background:
            dayData.stressLevelColor && selectedView !== 'day' ? dayData.stressLevelColor : ''
        }"
        @click="selectedView === 'week' ? setSelectedView('day', data.date) : null"
      >
        <div class="mds-cell-template-day">{{ dayData.dayContent }}</div>
        <div>{{ dayData.weather }}</div>
        <div class="mds-cell-template-info" style="color: #634b67">
          Internal mtgs: {{ dayData.nrMeetings }}
        </div>
        <div class="mds-cell-template-info" style="color: #656d49">
          Client mtgs: {{ dayData.nrAppointments }}
        </div>
        <MbscButton cssClass="mds-cell-template-add" icon="plus"></MbscButton>
      </div>
    </template>

    <template #schedulerDay="data">
      {{ void (dayData = getDayTemplate(data)) }}
      <div
        class="mds-cell-template-cont"
        :style="{
          background:
            dayData.stressLevelColor && selectedView !== 'day' ? dayData.stressLevelColor : ''
        }"
        @click="selectedView === 'week' ? setSelectedView('day', data.date) : null"
      >
        <div class="mds-cell-template-day">{{ dayData.dayContent }}</div>
        <div>{{ dayData.weather }}</div>
        <div class="mds-cell-template-info" style="color: #634b67">
          Internal mtgs: {{ dayData.nrMeetings }}
        </div>
        <div class="mds-cell-template-info" style="color: #656d49">
          Client mtgs: {{ dayData.nrAppointments }}
        </div>
        <MbscButton cssClass="mds-cell-template-add" icon="plus"></MbscButton>
      </div>
    </template>

    <template #header>
      <MbscCalendarNav cssClass="mds-cell-template-nav"></MbscCalendarNav>
      <div class="mds-cell-template-view-controls mbsc-flex-1-0">
        <MbscButton
          color="secondary"
          startIcon="close"
          cssClass="mds-cell-template-back"
          @click="setSelectedView(previousView)"
          >Back to calendar</MbscButton
        >
        <div class="mds-cell-template-view-switch">
          <MbscSegmentedGroup
            v-model="selectedView"
            @change="(ev) => setSelectedView(ev.target.value)"
          >
            <MbscSegmented
              value="month"
              icon="material-date-range"
              cssClass="mds-cell-template-view"
              >Calendar</MbscSegmented
            >
            <MbscSegmented value="week" icon="material-event-note" cssClass="mds-cell-template-view"
              >Week view</MbscSegmented
            >
          </MbscSegmentedGroup>
        </div>
      </div>
      <MbscCalendarPrev></MbscCalendarPrev>
      <MbscCalendarToday></MbscCalendarToday>
      <MbscCalendarNext></MbscCalendarNext>
    </template>
  </MbscEventcalendar>
  <MbscToast :isOpen="isToastOpen" :message="toastMessage" @close="isToastOpen = false" />
</template>

<style>
/* Overrides */

.mds-cell-template .mbsc-calendar-week-days,
.mds-cell-template .mbsc-calendar-labels {
  display: none;
}

.mds-cell-template .mbsc-calendar-cell {
  min-height: 120px;
}

.mds-cell-template .mbsc-calendar-day-outer {
  opacity: 0.7;
}

.mds-cell-template .mbsc-schedule-header-item {
  min-height: 120px;
  padding: 0;
  cursor: pointer;
}

/* Custom header */

.mds-cell-template-nav {
  width: 200px;
}

.mds-cell-template-view-controls {
  max-width: 350px;
  margin: 0 auto;
}

.mds-cell-template-view-switch .mbsc-segmented {
  margin: 0;
}

.mds-cell-template-view-switch .mbsc-segmented.mbsc-material,
.mds-cell-template-view-switch .mbsc-segmented.mbsc-windows {
  padding: 0;
}

.mds-cell-template-back.mbsc-button {
  width: 100%;
  height: 32px;
  margin: 0 6px;
  padding: 0 12px;
  display: none;
}

.mds-cell-template-back .mbsc-button-txt {
  font-size: 14px;
  line-height: 32px;
}

.mds-cell-template-back .mbsc-icon {
  width: 18px;
  height: 18px;
  line-height: 18px;
  font-size: 14px;
}

/* Cell template */

.mds-cell-template-cont {
  position: absolute;
  inset: -1px;
  overflow: hidden;
  padding: 10px;
  text-align: left;
  line-height: 25px;
  white-space: nowrap;
}

.mds-cell-template-cont:after {
  content: '';
  position: absolute;
  inset: -1px;
  pointer-events: none;
  transition: background 0.15s ease-in-out;
}

.mds-cell-template-cont:hover:after {
  background: rgba(51, 51, 51, 0.2);
}

.mds-cell-template-day {
  font-weight: 600;
}

.mds-cell-template-info {
  font-weight: 600;
  font-size: 15px;
  color: #555;
}

.mds-cell-template-add.mbsc-button {
  position: absolute;
  bottom: 5px;
  right: 5px;
  font-size: 10px;
  display: none;
}

.mbsc-calendar-width-sm .mds-cell-template-cont {
  font-size: 14px;
}

.mbsc-calendar-width-sm .mds-cell-template-info {
  font-size: 12px;
}

/* Month view */

.mds-cell-template-month-view.mbsc-windows .mds-cell-template-cont {
  inset: -2px -1px;
}

.mds-cell-template-month-view .mds-cell-template-add {
  display: block;
}

/* Day view */

.mds-cell-template-day-view .mds-cell-template-back {
  display: block;
}

.mds-cell-template-day-view .mbsc-schedule-header-item:not(.mbsc-selected),
.mds-cell-template-day-view .mds-cell-template-view-switch,
.mds-cell-template-day-view .mds-cell-template-cont:after {
  display: none;
}

.mds-cell-template-day-view .mds-cell-template-cont {
  text-align: center;
}

.mds-cell-template-day-view .mbsc-schedule-header-item.mbsc-selected {
  border: 0;
}
</style>
```

### SFC TS

```vue
<script setup lang="ts">
import {
  formatDate,
  MbscButton,
  MbscCalendarNav,
  MbscCalendarNext,
  MbscCalendarPrev,
  MbscCalendarToday,
  MbscEventcalendar,
  MbscSegmented,
  MbscSegmentedGroup,
  MbscToast,
  setOptions
} from '@mobiscroll/vue'
import type {
  MbscCalendarEvent,
  MbscCellClickEvent,
  MbscDateType,
  MbscEventcalendarView,
  MbscSelectedDateChangeEvent
} from '@mobiscroll/vue'
import { ref } from 'vue'

setOptions({
  theme: 'ios',
  themeVariant: 'light'
})

interface WeatherData {
  date: Date
  degree: number
  emoji: string
}

const myCssClass = ref<string>('mds-cell-template mds-cell-template-month-view')
const weatherCache = ref<Record<number, WeatherData>>({})
const selectedView = ref<string>('month')
const previousView = ref<string>('month')
const currentDate = ref<MbscDateType>(new Date())
const isToastOpen = ref<boolean>(false)
const toastMessage = ref<string>('')

const myEvents = ref<MbscCalendarEvent[]>([
  {
    start: '2026-10-23T15:00',
    end: '2026-10-23T17:00',
    title: 'Operations Huddle',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-23T12:00',
    end: '2026-10-23T13:00',
    title: 'HR Policy Update',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-24T08:00',
    end: '2026-10-24T10:00',
    title: 'Team Retrospective',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-24T10:00',
    end: '2026-10-24T11:30',
    title: 'Proposal Review Meeting',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-24T12:00',
    end: '2026-10-24T13:00',
    title: 'Solutions Presentation',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-24T14:00',
    end: '2026-10-24T15:00',
    title: 'Follow-up Discussion',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-24T15:00',
    end: '2026-10-24T16:00',
    title: 'Performance Review',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-25T10:00',
    end: '2026-10-25T13:00',
    title: 'Client Onboarding',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-25T15:00',
    end: '2026-10-25T16:00',
    title: 'Marketing Campaign Brainstorm',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-02T10:00',
    end: '2026-10-02T12:00',
    title: 'Innovation Brainstorm',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-02T13:00',
    end: '2026-10-02T15:30',
    title: 'Onboarding Session',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-02T16:00',
    end: '2026-10-02T17:00',
    title: 'Discovery Call',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-09T09:00',
    end: '2026-10-09T10:00',
    title: 'Partnership Exploration',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-09T11:00',
    end: '2026-10-09T13:00',
    title: 'Service Implementation',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-09T14:00',
    end: '2026-10-09T15:00',
    title: 'Future Planning Summit',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-15T10:00',
    end: '2026-10-15T12:00',
    title: 'Strategy Alignment',
    type: 'meeting',
    color: '#634b67'
  },
  {
    start: '2026-10-18T12:00',
    end: '2026-10-18T14:00',
    title: 'Project Kick-off',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-18T15:00',
    end: '2026-10-18T16:00',
    title: 'Account Review',
    type: 'appointment',
    color: '#656d49'
  },
  {
    start: '2026-10-22T12:00',
    end: '2026-10-22T14:00',
    title: 'Deep Dive Session',
    type: 'meeting',
    color: '#634b67'
  },
  {
    title: 'Fresh start meeting',
    start: '09:00',
    end: '10:00',
    type: 'meeting',
    color: '#634b67',
    recurring: {
      repeat: 'weekly',
      weekDays: 'MO'
    }
  },
  {
    title: 'Weekly wrapup',
    start: '16:00',
    end: '16:30',
    type: 'meeting',
    color: '#634b67',
    recurring: {
      repeat: 'weekly',
      weekDays: 'FR'
    }
  }
])

const myView = ref<MbscEventcalendarView>({
  calendar: {
    type: 'month'
  }
})

let dayData: {
  dayContent: string
  weather: string
  stressLevelColor: string
  nrMeetings: number
  nrAppointments: number
}

function myDefaultEvent(): MbscCalendarEvent {
  return {
    title: 'New appointment',
    type: 'appointment',
    color: '#656d49'
  }
}

function getWeatherForDate(date: Date): WeatherData {
  const key = date.getTime()
  if (!weatherCache.value[key]) {
    weatherCache.value[key] = generateRandomWeather(date)
  }
  return weatherCache.value[key]
}

function generateRandomWeather(date: Date): WeatherData {
  const weatherTypes = [
    { emoji: '☀️', min: 24, max: 30 },
    { emoji: '🌤️', min: 20, max: 25 },
    { emoji: '☁️', min: 17, max: 22 },
    { emoji: '🌧️', min: 15, max: 20 }
  ]
  const type = weatherTypes[Math.floor(Math.random() * weatherTypes.length)]
  const degree = Math.floor(Math.random() * (type.max - type.min + 1)) + type.min

  return {
    date: date,
    degree: degree,
    emoji: type.emoji
  }
}

function getStressLevel(nrEvents: number) {
  let emoji: string
  let color: string

  if (nrEvents < 1) {
    emoji = ''
    color = ''
  } else if (nrEvents < 3) {
    emoji = '😃'
    color = '#6ece86ff'
  } else if (nrEvents < 5) {
    emoji = '😐'
    color = '#d89c6aff'
  } else {
    emoji = '😫'
    color = '#d6727aff'
  }
  return { emoji: emoji, color: color }
}

function getNrEvents(events: MbscCalendarEvent[]) {
  let nrMeetings = 0
  let nrAppointments = 0

  for (const event of events) {
    if (event['type'] === 'meeting') {
      nrMeetings++
    } else {
      nrAppointments++
    }
  }

  return { meetings: nrMeetings, appointments: nrAppointments }
}

function setSelectedView(view: string, date?: Date) {
  if (view === 'day') {
    previousView.value = selectedView.value
  }

  selectedView.value = view

  switch (view) {
    case 'month':
      myCssClass.value = 'mds-cell-template mds-cell-template-month-view'
      myView.value = {
        calendar: { type: 'month' }
      }
      break
    case 'week':
      myCssClass.value = 'mds-cell-template mds-cell-template-week-view'
      myView.value = {
        scheduler: {
          type: 'week',
          allDay: false,
          startTime: '08:00',
          endTime: '17:00'
        }
      }
      break
    case 'day':
      myCssClass.value = 'mds-cell-template mds-cell-template-day-view'
      myView.value = {
        scheduler: {
          type: 'day',
          allDay: false,
          startTime: '08:00',
          endTime: '17:00'
        }
      }
      break
  }

  if (date) {
    currentDate.value = date
  }
}

function handleCellClick(args: MbscCellClickEvent) {
  const date = args.date
  const target = args.domEvent.target as HTMLElement

  if (target.closest('.mds-cell-template-add')) {
    const year = date.getFullYear()
    const month = date.getMonth()
    const day = date.getDate()

    const newEvent = {
      title: 'New appointment',
      start: new Date(year, month, day, 9),
      end: new Date(year, month, day, 10),
      color: '#ecee8d'
    }

    myEvents.value = [...myEvents.value, newEvent]

    toastMessage.value = 'Appointment added to ' + formatDate('DDD D, MMM', date)
    isToastOpen.value = true
  } else if (selectedView.value === 'month') {
    setSelectedView('day', date)
  }
}

function getDayTemplate(args: { date: Date; events: MbscCalendarEvent[] }) {
  const date = args.date
  const nrEvents = getNrEvents(args.events)
  const nrAllEvents = args.events.length
  const stressLevel = getStressLevel(nrAllEvents)
  const weather = getWeatherForDate(date)

  return {
    dayContent: formatDate('DDD D, MMM', date) + ' ' + stressLevel.emoji,
    weather: weather.emoji + ' ' + weather.degree + '°C',
    stressLevelColor: stressLevel.color,
    nrMeetings: nrEvents.meetings,
    nrAppointments: nrEvents.appointments
  }
}

function handleSelectedDateChange(args: MbscSelectedDateChangeEvent) {
  currentDate.value = args.date
}
</script>

<template>
  <MbscEventcalendar
    :clickToCreate="true"
    :dragToCreate="true"
    :dragToMove="true"
    :dragToResize="true"
    :eventDelete="true"
    :cssClass="myCssClass"
    :view="myView"
    :data="myEvents"
    :extendDefaultEvent="myDefaultEvent"
    :selectedDate="currentDate"
    @selected-date-change="handleSelectedDateChange"
    @cell-click="handleCellClick"
  >
    <template #calendarDay="data">
      {{ void (dayData = getDayTemplate(data)) }}
      <div
        class="mds-cell-template-cont"
        :style="{
          background:
            dayData.stressLevelColor && selectedView !== 'day' ? dayData.stressLevelColor : ''
        }"
        @click="selectedView === 'week' ? setSelectedView('day', data.date) : null"
      >
        <div class="mds-cell-template-day">{{ dayData.dayContent }}</div>
        <div>{{ dayData.weather }}</div>
        <div class="mds-cell-template-info" style="color: #634b67">
          Internal mtgs: {{ dayData.nrMeetings }}
        </div>
        <div class="mds-cell-template-info" style="color: #656d49">
          Client mtgs: {{ dayData.nrAppointments }}
        </div>
        <MbscButton cssClass="mds-cell-template-add" icon="plus"></MbscButton>
      </div>
    </template>

    <template #schedulerDay="data">
      {{ void (dayData = getDayTemplate(data)) }}
      <div
        class="mds-cell-template-cont"
        :style="{
          background:
            dayData.stressLevelColor && selectedView !== 'day' ? dayData.stressLevelColor : ''
        }"
        @click="selectedView === 'week' ? setSelectedView('day', data.date) : null"
      >
        <div class="mds-cell-template-day">{{ dayData.dayContent }}</div>
        <div>{{ dayData.weather }}</div>
        <div class="mds-cell-template-info" style="color: #634b67">
          Internal mtgs: {{ dayData.nrMeetings }}
        </div>
        <div class="mds-cell-template-info" style="color: #656d49">
          Client mtgs: {{ dayData.nrAppointments }}
        </div>
        <MbscButton cssClass="mds-cell-template-add" icon="plus"></MbscButton>
      </div>
    </template>

    <template #header>
      <MbscCalendarNav cssClass="mds-cell-template-nav"></MbscCalendarNav>
      <div class="mds-cell-template-view-controls mbsc-flex-1-0">
        <MbscButton
          color="secondary"
          startIcon="close"
          cssClass="mds-cell-template-back"
          @click="setSelectedView(previousView)"
          >Back to calendar</MbscButton
        >
        <div class="mds-cell-template-view-switch">
          <MbscSegmentedGroup
            v-model="selectedView"
            @change="(ev) => setSelectedView(ev.target.value)"
          >
            <MbscSegmented
              value="month"
              icon="material-date-range"
              cssClass="mds-cell-template-view"
              >Calendar</MbscSegmented
            >
            <MbscSegmented value="week" icon="material-event-note" cssClass="mds-cell-template-view"
              >Week view</MbscSegmented
            >
          </MbscSegmentedGroup>
        </div>
      </div>
      <MbscCalendarPrev></MbscCalendarPrev>
      <MbscCalendarToday></MbscCalendarToday>
      <MbscCalendarNext></MbscCalendarNext>
    </template>
  </MbscEventcalendar>
  <MbscToast :isOpen="isToastOpen" :message="toastMessage" @close="isToastOpen = false" />
</template>

<style>
/* Overrides */

.mds-cell-template .mbsc-calendar-week-days,
.mds-cell-template .mbsc-calendar-labels {
  display: none;
}

.mds-cell-template .mbsc-calendar-cell {
  min-height: 120px;
}

.mds-cell-template .mbsc-calendar-day-outer {
  opacity: 0.7;
}

.mds-cell-template .mbsc-schedule-header-item {
  min-height: 120px;
  padding: 0;
  cursor: pointer;
}

/* Custom header */

.mds-cell-template-nav {
  width: 200px;
}

.mds-cell-template-view-controls {
  max-width: 350px;
  margin: 0 auto;
}

.mds-cell-template-view-switch .mbsc-segmented {
  margin: 0;
}

.mds-cell-template-view-switch .mbsc-segmented.mbsc-material,
.mds-cell-template-view-switch .mbsc-segmented.mbsc-windows {
  padding: 0;
}

.mds-cell-template-back.mbsc-button {
  width: 100%;
  height: 32px;
  margin: 0 6px;
  padding: 0 12px;
  display: none;
}

.mds-cell-template-back .mbsc-button-txt {
  font-size: 14px;
  line-height: 32px;
}

.mds-cell-template-back .mbsc-icon {
  width: 18px;
  height: 18px;
  line-height: 18px;
  font-size: 14px;
}

/* Cell template */

.mds-cell-template-cont {
  position: absolute;
  inset: -1px;
  overflow: hidden;
  padding: 10px;
  text-align: left;
  line-height: 25px;
  white-space: nowrap;
}

.mds-cell-template-cont:after {
  content: '';
  position: absolute;
  inset: -1px;
  pointer-events: none;
  transition: background 0.15s ease-in-out;
}

.mds-cell-template-cont:hover:after {
  background: rgba(51, 51, 51, 0.2);
}

.mds-cell-template-day {
  font-weight: 600;
}

.mds-cell-template-info {
  font-weight: 600;
  font-size: 15px;
  color: #555;
}

.mds-cell-template-add.mbsc-button {
  position: absolute;
  bottom: 5px;
  right: 5px;
  font-size: 10px;
  display: none;
}

.mbsc-calendar-width-sm .mds-cell-template-cont {
  font-size: 14px;
}

.mbsc-calendar-width-sm .mds-cell-template-info {
  font-size: 12px;
}

/* Month view */

.mds-cell-template-month-view.mbsc-windows .mds-cell-template-cont {
  inset: -2px -1px;
}

.mds-cell-template-month-view .mds-cell-template-add {
  display: block;
}

/* Day view */

.mds-cell-template-day-view .mds-cell-template-back {
  display: block;
}

.mds-cell-template-day-view .mbsc-schedule-header-item:not(.mbsc-selected),
.mds-cell-template-day-view .mds-cell-template-view-switch,
.mds-cell-template-day-view .mds-cell-template-cont:after {
  display: none;
}

.mds-cell-template-day-view .mds-cell-template-cont {
  text-align: center;
}

.mds-cell-template-day-view .mbsc-schedule-header-item.mbsc-selected {
  border: 0;
}
</style>
```

## Implementation notes

- Start in month calendar view (`calendar: { type: 'month' }`) and support switching to week and day scheduler views (`scheduler: { type: 'week'/'day', allDay: false, startTime: '08:00', endTime: '17:00' }`) via a `SegmentedGroup` toggle in the custom header.
- Enable `clickToCreate`, `dragToCreate`, `dragToMove`, `dragToResize`, and `eventDelete` for full event management in the scheduler views. Use `extendDefaultEvent` to tag drag-created events with `type: 'appointment'` and the correct color so they are counted correctly in the cell renderer.
- Tag each event with a custom `type` property (`'meeting'` or `'appointment'`) so per-day counts can be split by category in the custom cell renderer.
- Use `renderCalendarDay` (Angular: `calendarDayTemplate`, Vue: `calendarDay`) to fully replace each calendar cell: the renderer receives `date` and `events` for that day. Count meetings vs appointments, compute a stress-level emoji and background color from total event count (0: none; 1-2: green/😃; 3-4: orange/😐; 5+: red/😫), generate and cache a random weather emoji and temperature per date, and render the day label, stress indicator, weather, meeting count, appointment count, and a plus button. Suppress the stress background color in day view.
- Use `renderHeader` (Angular: `headerTemplate`, Vue: `header`) to render the `SegmentedGroup` view toggle (Calendar / Week view), a "Back to calendar" button (visible only in day view), and the standard `CalendarNav`, `CalendarPrev`, `CalendarToday`, `CalendarNext` controls.
- In `onCellClick`, check whether the click target is the plus button: if so, create a new 09:00-10:00 event for that date and show a `Toast`; otherwise navigate to day view for the clicked date.
- Track a `previousView` state so the "Back to calendar" button returns to whichever view (month or week) the user was in before drilling into day view.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-vue.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config vue
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config vue --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/vue/getting-started/installation

## Related demos

- Previous: [Property Booking Calendar](https://demo.mobiscroll.com/vue/eventcalendar/property-booking-calendar.md) - Show property reservations in a monthly Vue booking calendar — exact check-in/check-out time labels, color-coded by platform (Airbnb, Booking).
