---
title: "Vue Event calendar Add, Edit and Delete Events Example | Mobiscroll"
component: "Event calendar"
framework: "Vue"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/vue/eventcalendar/create-read-update-delete-CRUD"
---

# Add, Edit and Delete Events - Vue Event calendar demo

Add full event management to a Vue event calendar — create events by drag or click, edit them in a custom dialog, and delete with undo. For Vue and Nuxt.

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

## Description

**Create new events**

Drag to create and click to create is enabled. Events can be created by dragging or with double clicks. As soon as the initial position is confirmed, a temporary event is created and a custom edit dialog is shown for refinement. On cancel the temporary event will be removed and on confirmation the event will stay in the calendar.

**Edit existing events**

Drag to resize and drag to move is enabled. Events can be reordered and resized. In addition to that, clicking on the event will open a custom dialog that enables editing the various properties.

**Delete events**

Delete can be implemented inside the edit dialog with a button. It's just a matter of removing it from the data object. If a dialog is not shown on click, focused events can be deleted with the *backspace* and *delete* keys.

**Interested in adding recurrence configuration?** [Check out the next example →](https://demo.mobiscroll.com/vue/eventcalendar/recurring-event-add-edit-dialog)

## What this demo shows

- A full month event calendar with multiple events and the standard header controls for month and year navigation, previous and next arrows, and a Today button.
- **Event creation** Users can create events by clicking and dragging across calendar cells or by double-clicking a day cell.
- **New event dialog** Creating an event opens a dialog with fields for title, description, all-day, start and end date selection, travel time, event color, and busy or free status.
- **Date selection** The start and end inputs open a date picker calendar with range selection.
- **Cancel behavior** Clicking outside the dialog or pressing Cancel closes it and removes the temporary event from the calendar.
- **Event editing** Clicking or tapping an existing event opens the same dialog in edit mode with the current values prefilled.
- **Editable properties** In edit mode, the event details and event color can be changed before saving.
- **Save and close behavior** Clicking outside the dialog or pressing Cancel closes the editor without applying changes, while Save updates the event.
- **Delete flow** The edit dialog includes a Delete event action that removes the event from the calendar.
- **Undo feedback** After deletion, a centered bottom toast appears with an Undo action so the deleted event can be restored.
- **Responsive behavior** The dialog adapts to screen size and device type, using touch-optimized controls such as scrollers on smaller or touch devices.
- **Mobile presentation** On smaller screens, the dialog is shown as a sheet instead of a popover.
- **Day cell states** Hovering a day cell highlights the day number with a gray background, while clicking the empty part of the cell selects the day and highlights the day number with a blue background.

## Best for

- **Monthly event management** Managing create, read, update, and delete workflows in a month-based calendar view.
- **General scheduling** Applications where users need to add, edit, and remove events directly from the calendar.
- **Mixed event types** Calendars that need to handle single-day, multi-day, all-day, and timed events in the same interface.
- **Responsive scheduling UIs** Products that need the same event management flow to work well on desktop and touch devices.

## 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
- [`labels`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-labels) - option
- [`cssClass`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-cssClass) - option
- [`navigateToEvent`](https://mobiscroll.com/docs/vue/eventcalendar/api#method-navigateToEvent) - method
- [`display`](https://mobiscroll.com/docs/vue/popup/api#opt-display) - option
- [`width`](https://mobiscroll.com/docs/vue/popup/api#opt-width) - option
- [`fullScreen`](https://mobiscroll.com/docs/vue/popup/api#opt-fullScreen) - option
- [`touchUi`](https://mobiscroll.com/docs/vue/popup/api#opt-touchUi) - option
- [`cssClass`](https://mobiscroll.com/docs/vue/popup/api#opt-cssClass) - option
- [`buttons`](https://mobiscroll.com/docs/vue/popup/api#opt-buttons) - 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
- [`data`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-data) - option
- [`view`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-view) - option
- [`event-click`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventClick) - event
- [`event-created`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventCreated) - event
- [`event-deleted`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventDeleted) - event
- [`scrollLock`](https://mobiscroll.com/docs/vue/popup/api#opt-scrollLock) - option
- [`headerText`](https://mobiscroll.com/docs/vue/popup/api#opt-headerText) - option
- [`anchor`](https://mobiscroll.com/docs/vue/popup/api#opt-anchor) - option
- [`responsive`](https://mobiscroll.com/docs/vue/popup/api#opt-responsive) - option
- [`isOpen`](https://mobiscroll.com/docs/vue/popup/api#opt-isOpen) - option
- [`close`](https://mobiscroll.com/docs/vue/popup/api#event-onClose) - event
- [`select`](https://mobiscroll.com/docs/vue/datepicker/api#opt-select) - option
- [`controls`](https://mobiscroll.com/docs/vue/datepicker/api#opt-controls) - option
- [`responsive`](https://mobiscroll.com/docs/vue/datepicker/api#opt-responsive) - option
- [`startInput`](https://mobiscroll.com/docs/vue/datepicker/api#opt-startInput) - option
- [`endInput`](https://mobiscroll.com/docs/vue/datepicker/api#opt-endInput) - option
- [`cssClass`](https://mobiscroll.com/docs/vue/forms/button#opt-cssClass) - option
- [`close`](https://mobiscroll.com/docs/vue/notifications/snackbar#event-onClose) - event
- [`showArrow`](https://mobiscroll.com/docs/vue/popup/api#opt-showArrow) - option
- [`showOverlay`](https://mobiscroll.com/docs/vue/popup/api#opt-showOverlay) - option

## Code

### SFC JS

```vue
<script setup>
import {
  MbscButton,
  MbscDatepicker,
  MbscDropdown,
  MbscEventcalendar,
  MbscInput,
  MbscPopup,
  MbscSegmented,
  MbscSegmentedGroup,
  MbscSnackbar,
  MbscSwitch,
  MbscTextarea,
  setOptions
} from '@mobiscroll/vue'
import { ref } from 'vue'

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

const calendar = ref(null)
const addEditPopup = ref(null)
const colorPicker = ref(null)

const eventId = ref(null)
const eventTitle = ref('')
const eventDescription = ref('')
const eventAllDay = ref(false)
const eventDates = ref([])
const startInput = ref(null)
const endInput = ref(null)
const eventBuffer = ref(0)
const eventColor = ref('')
const eventStatus = ref(false)

const selectedColor = ref('')
const statusValue = ref('busy')
const editedEvent = ref(null)
const isAddEditPopupOpen = ref(false)
const addEditPopupAnchor = ref(null)
const isColorPickerOpen = ref(false)
const colorPickerAnchor = ref(null)
const isEdit = ref(false)
const isSuccess = ref(false)

const isSnackbarOpen = ref(false)
const snackbarButton = {
  action: () => {
    myEvents.value = [...myEvents.value, editedEvent.value]
  },
  text: 'Undo'
}

const myView = { calendar: { labels: true } }

const colors = [
  '#ffeb3c',
  '#ff9900',
  '#f44437',
  '#ea1e63',
  '#9c26b0',
  '#3f51b5',
  '',
  '#009788',
  '#4baf4f',
  '#7e5d4e'
]

const myEvents = ref([
  {
    id: 1,
    start: '2026-10-08T13:00',
    end: '2026-10-08T13:45',
    title: "Lunch @ Butcher's",
    description: '',
    allDay: false,
    bufferBefore: 15,
    free: true,
    color: '#009788'
  },
  {
    id: 2,
    start: '2026-10-26T15:00',
    end: '2026-10-26T16:00',
    title: 'Conference',
    description: '',
    allDay: false,
    bufferBefore: 30,
    free: false,
    color: '#ff9900'
  },
  {
    id: 3,
    start: '2026-10-25T18:00',
    end: '2026-10-25T22:00',
    title: 'Site Visit',
    description: '',
    allDay: false,
    bufferBefore: 60,
    free: true,
    color: '#3f51b5'
  },
  {
    id: 4,
    start: '2026-10-27T10:30',
    end: '2026-10-27T11:30',
    title: 'Stakeholder mtg.',
    description: '',
    allDay: false,
    free: false,
    color: '#f44437'
  }
])

const editButtons = [
  'cancel',
  {
    text: 'Save',
    keyCode: 'enter',
    cssClass: 'mbsc-popup-button-primary',
    handler: () => {
      const updatedEvent = getEventData()
      const index = myEvents.value.findIndex((x) => x.id === updatedEvent.id)
      const newEventList = [...myEvents.value]

      // Update event in the list
      newEventList.splice(index, 1, updatedEvent)
      myEvents.value = newEventList

      calendar.value.instance.navigateToEvent(updatedEvent)
      isAddEditPopupOpen.value = false
    }
  }
]

const addButtons = [
  'cancel',
  {
    text: 'Add',
    keyCode: 'enter',
    cssClass: 'mbsc-popup-button-primary',
    handler: () => {
      const newEvent = getEventData()

      // Add new event to the list
      myEvents.value = [...myEvents.value, newEvent]

      isSuccess.value = true
      calendar.value.instance.navigateToEvent(newEvent)
      isAddEditPopupOpen.value = false
    }
  }
]

const addEditPopupResponsive = {
  medium: {
    display: 'anchored',
    width: 400,
    fullScreen: false,
    touchUi: false
  }
}

const colorPickerButtons = [
  'cancel',
  {
    text: 'Set',
    keyCode: 'enter',
    handler: () => applySelectedColor(selectedColor.value),
    cssClass: 'mbsc-popup-button-primary'
  }
]

const colorPickerResponsive = {
  medium: {
    display: 'anchored',
    buttons: [],
    touchUi: false
  }
}

function fillPopup(event) {
  eventId.value = event.id
  eventTitle.value = event.title || ''
  eventDescription.value = event.description || ''
  eventAllDay.value = event.allDay
  eventDates.value = [event.start, event.end]
  eventBuffer.value = event.bufferBefore || 0
  eventColor.value = event.color || ''
  eventStatus.value = event.free || false
  statusValue.value = event.free ? 'free' : 'busy'
}

function createEditPopup(event, target) {
  isEdit.value = true
  editedEvent.value = event
  addEditPopupAnchor.value = target
  fillPopup(event)
  isAddEditPopupOpen.value = true
}

function createAddPopup(event, target) {
  isSuccess.value = false
  isEdit.value = false
  editedEvent.value = event
  addEditPopupAnchor.value = target
  fillPopup({ ...event, allDay: false })
  isAddEditPopupOpen.value = true
}

function getEventData() {
  return {
    id: eventId.value,
    title: eventTitle.value,
    description: eventDescription.value,
    allDay: eventAllDay.value,
    start: eventDates.value[0],
    end: eventDates.value[1],
    bufferBefore: eventBuffer.value,
    color: eventColor.value,
    free: statusValue.value === 'free'
  }
}

function handleAddEditPopupClose() {
  if (!isEdit.value && !isSuccess.value) {
    // Refresh the list, if add popup was canceled, to remove the temporary event
    myEvents.value = [...myEvents.value]
  }
  isAddEditPopupOpen.value = false
}

function handleEventClick(args) {
  createEditPopup(args.event, args.domEvent.currentTarget)
}

function handleEventCreated(args) {
  setTimeout(() => {
    createAddPopup(args.event, args.target)
  })
}

function handleEventDeleted() {
  isSnackbarOpen.value = true
}

function handleDeleteButtonClick() {
  const filteredEvents = myEvents.value.filter((e) => e.id !== editedEvent.value.id)
  myEvents.value = filteredEvents
  isAddEditPopupOpen.value = false
  isSnackbarOpen.value = true
}

function handleEventColorClick(ev) {
  colorPickerAnchor.value = ev.currentTarget
  isColorPickerOpen.value = true
}

function handleColorChange(ev) {
  const color = ev.currentTarget.getAttribute('data-value')
  eventColor.value = color
  selectedColor.value = color
  if (!colorPicker.value.instance.s.buttons.length) {
    applySelectedColor(color)
  }
}

function applySelectedColor(color) {
  eventColor.value = color
  isColorPickerOpen.value = false
}

function handleColorPickerClose() {
  isColorPickerOpen.value = false
}

function handleSnackbarClose() {
  isSnackbarOpen.value = false
}
</script>

<template>
  <MbscEventcalendar
    ref="calendar"
    :clickToCreate="true"
    :dragToCreate="true"
    :dragToMove="true"
    :dragToResize="true"
    :data="myEvents"
    :view="myView"
    @event-click="handleEventClick"
    @event-created="handleEventCreated"
    @event-deleted="handleEventDeleted"
  />
  <MbscPopup
    ref="addEditPopup"
    display="bottom"
    :contentPadding="false"
    :fullScreen="true"
    :scrollLock="false"
    :headerText="isEdit ? 'Edit event' : 'New Event'"
    :buttons="isEdit ? editButtons : addButtons"
    :anchor="addEditPopupAnchor"
    :responsive="addEditPopupResponsive"
    :isOpen="isAddEditPopupOpen"
    @close="handleAddEditPopupClose"
  >
    <div class="mbsc-form-group">
      <MbscInput label="Title" v-model="eventTitle" />
      <MbscTextarea label="Description" v-model="eventDescription" />
    </div>
    <div class="mbsc-form-group">
      <div>
        <MbscSwitch label="All-day" v-model="eventAllDay" />

        <MbscDatepicker
          v-model="eventDates"
          select="range"
          :controls="eventAllDay ? ['date'] : ['datetime']"
          :responsive="
            eventAllDay
              ? { medium: { controls: ['calendar'], touchUi: false } }
              : { medium: { controls: ['calendar', 'time'], touchUi: false } }
          "
          :startInput="startInput"
          :endInput="endInput"
        />

        <MbscInput ref="startInput" label="Starts" />
        <MbscInput ref="endInput" label="Ends" />

        <MbscDropdown v-if="!eventAllDay" v-model="eventBuffer" label="Travel time">
          <option value="0">None</option>
          <option value="5">5 minutes</option>
          <option value="15">15 minutes</option>
          <option value="30">30 minutes</option>
          <option value="60">1 hour</option>
          <option value="90">1.5 hours</option>
          <option value="120">2 hours</option>
        </MbscDropdown>
      </div>

      <div class="mbsc-flex mds-crud-event-color-cont" @click="handleEventColorClick($event)">
        <div class="mbsc-flex-1-0">Color</div>
        <div class="mds-crud-selected-event-color" :style="{ background: eventColor }"></div>
      </div>

      <MbscSegmentedGroup v-model="statusValue">
        <MbscSegmented value="busy">Show as busy</MbscSegmented>
        <MbscSegmented value="free">Show as free</MbscSegmented>
      </MbscSegmentedGroup>

      <div v-if="isEdit" class="mbsc-button-group">
        <MbscButton
          cssClass="mbsc-button-block"
          color="danger"
          variant="outline"
          @click="handleDeleteButtonClick"
          >Delete event
        </MbscButton>
      </div>
    </div>
  </MbscPopup>

  <MbscSnackbar
    message="Event deleted"
    :button="snackbarButton"
    :isOpen="isSnackbarOpen"
    @close="handleSnackbarClose"
  />

  <MbscPopup
    ref="colorPicker"
    display="bottom"
    :anchor="colorPickerAnchor"
    :contentPadding="false"
    :showArrow="false"
    :showOverlay="false"
    :buttons="colorPickerButtons"
    :responsive="colorPickerResponsive"
    :isOpen="isColorPickerOpen"
    @close="handleColorPickerClose"
  >
    <div class="mbsc-flex mds-crud-color-row">
      <div v-for="(color, i) in colors" :key="color">
        <div
          v-if="i < 5"
          class="mds-crud-color-value"
          :class="{ 'mds-crud-color-value-selected': eventColor === color }"
          :data-value="color"
          @click="handleColorChange($event)"
        >
          <div
            class="mds-crud-color mbsc-icon mbsc-font-icon mbsc-icon-material-check"
            :style="{ background: color }"
          ></div>
        </div>
      </div>
    </div>
    <div class="mbsc-flex mds-crud-color-row">
      <div v-for="(color, i) in colors" :key="color">
        <div
          v-if="i >= 5"
          class="mds-crud-color-value"
          :class="{ 'mds-crud-color-value-selected': eventColor === color }"
          :data-value="color"
          @click="handleColorChange($event)"
        >
          <div
            class="mds-crud-color mbsc-icon mbsc-font-icon mbsc-icon-material-check"
            :style="{ background: color }"
          ></div>
        </div>
      </div>
    </div>
  </MbscPopup>
</template>

<style>
.mds-crud-event-color-cont {
  margin: 16px;
  align-items: center;
  cursor: pointer;
}

.mds-crud-selected-event-color {
  width: 30px;
  height: 30px;
  border-radius: 15px;
  margin-right: 10px;
  margin-left: 240px;
  background: #5ac8fa;
}

.mds-crud-color-row {
  justify-content: center;
  margin: 5px;
}

.mds-crud-color {
  position: relative;
  min-width: 46px;
  min-height: 46px;
  margin: 2px;
  cursor: pointer;
  border-radius: 23px;
  background: #5ac8fa;
}

.mds-crud-color:before {
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -10px;
  margin-left: -10px;
  color: #f7f7f7;
  font-size: 20px;
  text-shadow: 0 0 3px #000;
  display: none;
}

.mds-crud-color-value {
  padding: 3px;
  margin: 2px;
}

.mds-crud-color-value.mds-crud-color-value-selected,
.mds-crud-color-value:hover {
  box-shadow: inset 0 0 0 3px #007bff;
  border-radius: 48px;
}

.mds-crud-color-value.mds-crud-color-value-selected .mds-crud-color:before {
  display: block;
}
</style>
```

### SFC TS

```vue
<script setup lang="ts">
import {
  MbscButton,
  MbscDatepicker,
  MbscDropdown,
  MbscEventcalendar,
  MbscInput,
  MbscPopup,
  MbscSegmented,
  MbscSegmentedGroup,
  MbscSnackbar,
  MbscSwitch,
  MbscTextarea,
  setOptions
} from '@mobiscroll/vue'
import type {
  MbscCalendarEvent,
  MbscDateType,
  MbscEventcalendarView,
  MbscEventClickEvent,
  MbscEventCreatedEvent,
  MbscPopupButton,
  MbscPopupOptions,
  MbscResponsiveOptions
} from '@mobiscroll/vue'
import { ref } from 'vue'

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

const calendar = ref<typeof MbscEventcalendar>()
const colorPicker = ref<typeof MbscPopup>()

const eventId = ref<string | number | undefined>()
const eventTitle = ref<string>('')
const eventDescription = ref<string>('')
const eventAllDay = ref<boolean>(false)
const eventDates = ref<MbscDateType[]>([])
const startInput = ref<typeof MbscInput>()
const endInput = ref<typeof MbscInput>()
const eventBuffer = ref<number>(0)
const eventColor = ref<string>('')
const eventStatus = ref<boolean>(false)

const selectedColor = ref<string>('')
const statusValue = ref<string>('busy')
const editedEvent = ref<MbscCalendarEvent | null>(null)
const isAddEditPopupOpen = ref<boolean>(false)
const addEditPopupAnchor = ref<HTMLElement | undefined>()
const isColorPickerOpen = ref<boolean>(false)
const colorPickerAnchor = ref<HTMLElement | undefined>()
const isEdit = ref<boolean>(false)
const isSuccess = ref<boolean>(false)

const isSnackbarOpen = ref<boolean>(false)

const snackbarButton = {
  action: () => {
    myEvents.value = [...myEvents.value, editedEvent.value!]
  },
  text: 'Undo'
}

const myView: MbscEventcalendarView = { calendar: { labels: true } }

const colors: string[] = [
  '#ffeb3c',
  '#ff9900',
  '#f44437',
  '#ea1e63',
  '#9c26b0',
  '#3f51b5',
  '',
  '#009788',
  '#4baf4f',
  '#7e5d4e'
]

const myEvents = ref<MbscCalendarEvent[]>([
  {
    id: 1,
    start: '2026-10-08T13:00',
    end: '2026-10-08T13:45',
    title: "Lunch @ Butcher's",
    description: '',
    allDay: false,
    bufferBefore: 15,
    free: true,
    color: '#009788'
  },
  {
    id: 2,
    start: '2026-10-26T15:00',
    end: '2026-10-26T16:00',
    title: 'Conference',
    description: '',
    allDay: false,
    bufferBefore: 30,
    free: false,
    color: '#ff9900'
  },
  {
    id: 3,
    start: '2026-10-25T18:00',
    end: '2026-10-25T22:00',
    title: 'Site Visit',
    description: '',
    allDay: false,
    bufferBefore: 60,
    free: true,
    color: '#3f51b5'
  },
  {
    id: 4,
    start: '2026-10-27T10:30',
    end: '2026-10-27T11:30',
    title: 'Stakeholder mtg.',
    description: '',
    allDay: false,
    free: false,
    color: '#f44437'
  }
])

const editButtons: (MbscPopupButton | string)[] = [
  'cancel',
  {
    text: 'Save',
    keyCode: 'enter',
    cssClass: 'mbsc-popup-button-primary',
    handler: () => {
      const updatedEvent = getEventData()
      const index = myEvents.value.findIndex((x) => x.id === updatedEvent.id)
      const newEventList = [...myEvents.value]

      // Update event in the list
      newEventList.splice(index, 1, updatedEvent)
      myEvents.value = newEventList

      calendar.value?.instance.navigateToEvent(updatedEvent)
      isAddEditPopupOpen.value = false
    }
  }
]

const addButtons: (MbscPopupButton | string)[] = [
  'cancel',
  {
    text: 'Add',
    keyCode: 'enter',
    cssClass: 'mbsc-popup-button-primary',
    handler: () => {
      const newEvent = getEventData()

      // Add new event to the list
      myEvents.value = [...myEvents.value, newEvent]

      isSuccess.value = true
      calendar.value?.instance.navigateToEvent(newEvent)
      isAddEditPopupOpen.value = false
    }
  }
]

const addEditPopupResponsive: MbscResponsiveOptions<MbscPopupOptions> = {
  medium: {
    display: 'anchored',
    width: 400,
    fullScreen: false,
    touchUi: false
  }
}

const colorPickerButtons: (MbscPopupButton | string)[] = [
  'cancel',
  {
    text: 'Set',
    keyCode: 'enter',
    handler: () => applySelectedColor(selectedColor.value),
    cssClass: 'mbsc-popup-button-primary'
  }
]

const colorPickerResponsive: MbscResponsiveOptions<MbscPopupOptions> = {
  medium: {
    display: 'anchored',
    buttons: [],
    touchUi: false
  }
}

function fillPopup(event: MbscCalendarEvent) {
  eventId.value = event.id
  eventTitle.value = event.title || ''
  eventDescription.value = event.description || ''
  eventAllDay.value = event.allDay!
  eventDates.value = [event.start!, event.end!]
  eventBuffer.value = event.bufferBefore || 0
  eventColor.value = event.color || ''
  eventStatus.value = event.free || false
  statusValue.value = event.free ? 'free' : 'busy'
}

function createEditPopup(event: MbscCalendarEvent, target: HTMLElement) {
  isEdit.value = true
  editedEvent.value = event
  addEditPopupAnchor.value = target
  fillPopup(event)
  isAddEditPopupOpen.value = true
}

function createAddPopup(event: MbscCalendarEvent, target: HTMLElement) {
  isSuccess.value = false
  isEdit.value = false
  editedEvent.value = event
  addEditPopupAnchor.value = target
  fillPopup({ ...event, allDay: false })
  isAddEditPopupOpen.value = true
}

function getEventData() {
  return {
    id: eventId.value,
    title: eventTitle.value,
    description: eventDescription.value,
    allDay: eventAllDay.value,
    start: eventDates.value[0],
    end: eventDates.value[1],
    bufferBefore: eventBuffer.value,
    color: eventColor.value,
    free: statusValue.value === 'free'
  }
}

function handleAddEditPopupClose() {
  if (!isEdit.value && !isSuccess.value) {
    // Refresh the list, if add popup was canceled, to remove the temporary event
    myEvents.value = [...myEvents.value]
  }
  isAddEditPopupOpen.value = false
}

function handleEventClick(args: MbscEventClickEvent) {
  createEditPopup(args.event, args.domEvent.currentTarget)
}

function handleEventCreated(args: MbscEventCreatedEvent) {
  setTimeout(() => {
    createAddPopup(args.event, args.target!)
  })
}

function handleEventDeleted() {
  isSnackbarOpen.value = true
}

function handleDeleteButtonClick() {
  const filteredEvents = myEvents.value.filter((e) => e.id !== editedEvent.value!.id)
  myEvents.value = filteredEvents
  isAddEditPopupOpen.value = false
  isSnackbarOpen.value = true
}

function handleEventColorClick(ev: MouseEvent) {
  colorPickerAnchor.value = ev.currentTarget as HTMLElement
  isColorPickerOpen.value = true
}

function handleColorChange(ev: MouseEvent) {
  const color: string = (ev.currentTarget as HTMLDivElement).getAttribute('data-value') || ''
  eventColor.value = color
  selectedColor.value = color
  if (!colorPicker.value?.instance.s.buttons!.length) {
    applySelectedColor(color)
  }
}

function applySelectedColor(color: string) {
  eventColor.value = color
  isColorPickerOpen.value = false
}

function handleSnackbarClose() {
  isSnackbarOpen.value = false
}

function handleColorPickerClose() {
  isColorPickerOpen.value = false
}
</script>

<template>
  <MbscEventcalendar
    ref="calendar"
    :clickToCreate="true"
    :dragToCreate="true"
    :dragToMove="true"
    :dragToResize="true"
    :data="myEvents"
    :view="myView"
    @event-click="handleEventClick"
    @event-created="handleEventCreated"
    @event-deleted="handleEventDeleted"
  />
  <MbscPopup
    ref="addEditPopup"
    display="bottom"
    :contentPadding="false"
    :fullScreen="true"
    :scrollLock="false"
    :headerText="isEdit ? 'Edit event' : 'New Event'"
    :buttons="isEdit ? editButtons : addButtons"
    :anchor="addEditPopupAnchor"
    :responsive="addEditPopupResponsive"
    :isOpen="isAddEditPopupOpen"
    @close="handleAddEditPopupClose"
  >
    <div class="mbsc-form-group">
      <MbscInput label="Title" v-model="eventTitle" />
      <MbscTextarea label="Description" v-model="eventDescription" />
    </div>
    <div class="mbsc-form-group">
      <div>
        <MbscSwitch label="All-day" v-model="eventAllDay" />

        <MbscDatepicker
          v-model="eventDates"
          select="range"
          :controls="eventAllDay ? ['date'] : ['datetime']"
          :responsive="
            eventAllDay
              ? { medium: { controls: ['calendar'], touchUi: false } }
              : { medium: { controls: ['calendar', 'time'], touchUi: false } }
          "
          :startInput="startInput"
          :endInput="endInput"
        />

        <MbscInput ref="startInput" label="Starts" />
        <MbscInput ref="endInput" label="Ends" />

        <MbscDropdown v-if="!eventAllDay" v-model="eventBuffer" label="Travel time">
          <option value="0">None</option>
          <option value="5">5 minutes</option>
          <option value="15">15 minutes</option>
          <option value="30">30 minutes</option>
          <option value="60">1 hour</option>
          <option value="90">1.5 hours</option>
          <option value="120">2 hours</option>
        </MbscDropdown>
      </div>

      <div class="mbsc-flex mds-crud-event-color-cont" @click="handleEventColorClick($event)">
        <div class="mbsc-flex-1-0">Color</div>
        <div class="mds-crud-selected-event-color" :style="{ background: eventColor }"></div>
      </div>

      <MbscSegmentedGroup v-model="statusValue">
        <MbscSegmented value="busy">Show as busy</MbscSegmented>
        <MbscSegmented value="free">Show as free</MbscSegmented>
      </MbscSegmentedGroup>

      <div v-if="isEdit" class="mbsc-button-group">
        <MbscButton
          cssClass="mbsc-button-block"
          color="danger"
          variant="outline"
          @click="handleDeleteButtonClick"
          >Delete event
        </MbscButton>
      </div>
    </div>
  </MbscPopup>

  <MbscSnackbar
    message="Event deleted"
    :button="snackbarButton"
    :isOpen="isSnackbarOpen"
    @close="handleSnackbarClose"
  />

  <MbscPopup
    ref="colorPicker"
    display="bottom"
    :anchor="colorPickerAnchor"
    :contentPadding="false"
    :showArrow="false"
    :showOverlay="false"
    :buttons="colorPickerButtons"
    :responsive="colorPickerResponsive"
    :isOpen="isColorPickerOpen"
    @close="handleColorPickerClose"
  >
    <div class="mbsc-flex mds-crud-color-row">
      <div v-for="(color, i) in colors" :key="color">
        <div
          v-if="i < 5"
          class="mds-crud-color-value"
          :class="{ 'mds-crud-color-value-selected': eventColor === color }"
          :data-value="color"
          @click="handleColorChange($event)"
        >
          <div
            class="mds-crud-color mbsc-icon mbsc-font-icon mbsc-icon-material-check"
            :style="{ background: color }"
          ></div>
        </div>
      </div>
    </div>
    <div class="mbsc-flex mds-crud-color-row">
      <div v-for="(color, i) in colors" :key="color">
        <div
          v-if="i >= 5"
          class="mds-crud-color-value"
          :class="{ 'mds-crud-color-value-selected': eventColor === color }"
          :data-value="color"
          @click="handleColorChange($event)"
        >
          <div
            class="mds-crud-color mbsc-icon mbsc-font-icon mbsc-icon-material-check"
            :style="{ background: color }"
          ></div>
        </div>
      </div>
    </div>
  </MbscPopup>
</template>

<style>
.mds-crud-event-color-cont {
  margin: 16px;
  align-items: center;
  cursor: pointer;
}

.mds-crud-selected-event-color {
  width: 30px;
  height: 30px;
  border-radius: 15px;
  margin-right: 10px;
  margin-left: 240px;
  background: #5ac8fa;
}

.mds-crud-color-row {
  justify-content: center;
  margin: 5px;
}

.mds-crud-color {
  position: relative;
  min-width: 46px;
  min-height: 46px;
  margin: 2px;
  cursor: pointer;
  border-radius: 23px;
  background: #5ac8fa;
}

.mds-crud-color:before {
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -10px;
  margin-left: -10px;
  color: #f7f7f7;
  font-size: 20px;
  text-shadow: 0 0 3px #000;
  display: none;
}

.mds-crud-color-value {
  padding: 3px;
  margin: 2px;
}

.mds-crud-color-value.mds-crud-color-value-selected,
.mds-crud-color-value:hover {
  box-shadow: inset 0 0 0 3px #007bff;
  border-radius: 48px;
}

.mds-crud-color-value.mds-crud-color-value-selected .mds-crud-color:before {
  display: block;
}
</style>
```

## Implementation notes

- Use `view: { calendar: { labels: true } }`. Enable all four interaction modes: `clickToCreate` (React/Vue/Angular: `true`; JS/jQuery: `'double'`), `dragToCreate: true`, `dragToMove: true`, `dragToResize: true`.
- Start with 4 sample events using `dyndatetime` offsets. Each event carries: `id`, `title`, `description`, `allDay`, `start`, `end`, `bufferBefore`, `free`, and `color`.
- Handle three calendar events: `onEventClick` → opens the edit popup; `onEventCreated` → opens the add popup (the event is already temporarily placed on the calendar); `onEventDeleted` → shows a snackbar ("Event deleted") with an "Undo" button that adds the event back. Angular: use the `Notifications` service for the snackbar.
- Maintain an `isEdit` flag and an `editedEvent` reference. A `fillPopup(event)` helper populates all form fields from the event object.
- **Edit mode**: set `isEdit = true`, store `editedEvent`, set popup anchor to the clicked element, call `fillPopup`, open with "Edit event" header and a "Save" button.
- **Add mode**: set `isEdit = false`, set `isSuccess = false`, call `fillPopup`, open with "New Event" header and an "Add" button.
- **On popup close (add mode, not confirmed)**: if `isEdit` is false and `isSuccess` is false, refresh the events array (spread trick) to strip the temporary event; for the imperative API, call `calendar.removeEvent(event)` in the popup's `onClose` handler.
- **"Add" button**: build the event from form state, append to the events array, set `isSuccess = true`, call `inst.navigateToEvent(newEvent)`, close popup.
- **"Save" button**: build updated event from form state, find by `id` and splice in place, call `inst.navigateToEvent(updatedEvent)`, close popup.
- **Popup form fields**: **Title** (`Input`), **Description** (`Textarea`), **All-day** (`Switch` - when on, switches the date picker to date-only and removes the Travel time row), **Start/End** (a `Datepicker` in range mode with `startInput`/`endInput`; controls are `['datetime']` normally, `['date']` for all-day), **Travel time** (`Dropdown` with None/5/15/30 min/1 h/1.5 h/2 h options mapped to `bufferBefore` in minutes - hidden when all-day), **Color** (a clickable row opening a color picker popup with 10 swatches), **Show as busy / free** (`SegmentedGroup` mapping to the `free` boolean), and a **Delete event** button (visible in edit mode only).
- The color picker is a separate `Popup` with 10 color swatches in 2 rows of 5: `#ffeb3c`, `#ff9900`, `#f44437`, `#ea1e63`, `#9c26b0` / `#3f51b5`, empty string (no color), `#009788`, `#4baf4f`, `#7e5d4e`. On desktop (`display: 'anchored'`, no buttons): clicking applies and closes immediately. On mobile: bottom-sheet with "Set"/"Cancel". The active swatch is highlighted.
- **Delete from popup**: filter the event from the array by `id`, close the popup, show a snackbar with "Undo".

## 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

- Next: [Recurring Events Editor](https://demo.mobiscroll.com/vue/eventcalendar/recurring-event-add-edit-dialog.md) - Add recurring event management to a Vue event calendar — use preset daily, weekly, monthly, and yearly rules or a custom recurrence editor.
