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

# Create, Edit and Delete Events - Vue Scheduler demo

Implement full event CRUD in a Vue Scheduler — drag or click to create events, confirm via a custom dialog, then drag to move, resize, or delete. For Vue and Nuxt.

- [Live demo](https://demo.mobiscroll.com/vue/scheduler/create-read-update-delete-CRUD)
- [View SFC JS code on GitHub](https://github.com/acidb/mobiscroll-demos-vue/tree/main/src/demos/eventcalendar/scheduler/create-read-update-delete-CRUD)
- [View SFC TS code on GitHub](https://github.com/acidb/mobiscroll-demos-vue-ts/tree/main/src/demos/eventcalendar/scheduler/create-read-update-delete-CRUD)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/scheduler/create-read-update-delete-CRUD.md), [jQuery](https://demo.mobiscroll.com/jquery/scheduler/create-read-update-delete-CRUD.md), [Angular](https://demo.mobiscroll.com/angular/scheduler/create-read-update-delete-CRUD.md), [React](https://demo.mobiscroll.com/react/scheduler/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 this example →](https://demo.mobiscroll.com/vue/scheduler/recurring-event-add-edit-dialog)

## What this demo shows

- A desktop weekly scheduler layout with a fixed week strip at the top, a fixed all-day row below it, and a scrollable scheduler time grid for the selected week.
- **Header navigation** The month and year label in the top left opens date navigation, while the blue previous and next arrows and the Today button on the right make it easy to move between weeks and jump back to the current day.
- **Week view** The fixed strip below the header displays the days of the selected week, with the current date highlighted by a blue circle.
- **All-day events** All-day events appear in a dedicated row that stays fixed above the time grid.
- **Time grid** The scheduler grid scrolls vertically through the hours of the selected week.
- **Timed events** Timed events are displayed in the weekly scheduler grid as colored cards with a colored stripe on the left, the event title in bold, and the exact start and end time displayed above the title.
- **Event overlapping** If events overlap, the scheduler places them side by side without hiding any of them or causing other conflicts.
- **Current time** A blue current-time line appears across the time grid, with a small blue dot marking the current date.
- **Hover behavior** Hovering over the time grid shows a time indicator that follows the cursor in 15-minute increments.
- **Event interaction** Hovering over events highlights them and shows drag and resize handles for repositioning or changing duration.
- **Event creation** Double-clicking the time grid or clicking and dragging on it creates a new event.
- **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 scheduler.
- **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 scheduler.
- **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.

## Best for

- **Weekly event management** Managing create, read, update, and delete workflows in a week-based scheduler view.
- **General scheduling** Applications where users need to add, edit, and remove events directly from the scheduler.
- **Mixed event types** Schedulers 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
- [`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 = { scheduler: { type: 'week' } }

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)
  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 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>
```

### 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 = { scheduler: { type: 'week' } }

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)
  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: { scheduler: { type: 'week' } }`. 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.
- 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. On desktop: 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: [Bulk Event Actions](https://demo.mobiscroll.com/vue/scheduler/event-bulk-actions-edit-delete-update.md) - Add bulk event actions to Vue Scheduler — select multiple events via shortcuts or select-all, apply bulk actions, and use right-click for per-event actions.
