---
title: "Vue Timeline Multi-Select & Bulk Event Actions Example | Mobiscroll"
component: "Timeline"
framework: "Vue"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/vue/timeline/event-bulk-actions-edit-delete-update"
---

# Multi-Select & Bulk Event Actions - Vue Timeline demo

Select multiple events on a Vue Timeline by keyboard shortcut or button, then apply bulk delete or update — a side panel tracks the selection. For Vue and Nuxt.

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

## Description

Multiple event selection can be turned on with the [`selectMultipleEvents`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-selectMultipleEvents) option. Selecting multiple events can be done through user interaction with `CTRL`/`SHIFT`/`CMD` + click or programmatically eg. click of a button or 'select all' checkbox.

Bulk operations like delete, update can be applied the selected events. Things like deleting with the `backspace` or `delete` buttons work out of the box but custom actions can be also applied. The selection can be easily retrieved and updated with the [`selectedEvents`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-selectedEvents) option and the [`selected-events-change`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onSelectedEventsChange) event.

Custom actions can be performed with external buttons or with context menu activated on right-click.

## What this demo shows

- A desktop weekly timeline where days are arranged horizontally and resources are listed as rows on the left.
- **Header navigation** The month and year label opens date navigation, while the previous and next arrows move between weeks, and the Today button returns to the current date.
- **Week view** The strip below the header shows the selected week from Sunday to Saturday, with the current date highlighted.
- **Time grid** The timeline displays hourly columns from 12 AM to 12 PM.
- **Resources** Multiple resources are shown as separate timeline rows.
- **Current time** A vertical blue line with a time label marks the current time.
- **Hover behavior** Hovering over the time grid shows a time indicator that follows the cursor in 15-minute increments.
- **Bulk action panel** A side panel next to the timeline contains `Select all from view`, `Reset selection`, and `Update selected` actions.
- **Select all from view** Clicking this action selects all events currently visible, highlights their titles in the week view, and shows a bottom-centered toast message: `All events selected from the view`.
- **Reset selection** Clicking this action clears the current selection and shows a bottom-centered toast message: `Selection cleared`.
- **Update selected** Clicking this action changes the color of all selected events to orange and shows a bottom-centered toast message: `All selected event's color changed to orange`.
- **Currently selected** The side panel includes a `Currently selected` section that is empty by default and lists the titles of all selected events when one or more events are selected.
- **Multi-selection** Users can select multiple events with `CTRL`/`SHIFT`/`CMD` + click.
- **Context menu** Right-clicking an event opens a small popup with `Update` and `Delete` actions for that event.
- **Event cards** Events appear as colored cards with a colored stripe on the left, the event title in bold, and the exact start and end time displayed below the title.
- **Variable event height** Timeline events grow to fit their custom event content instead of forcing every event into the same fixed height.
- **Resource row height** Resource rows adjust dynamically based on the height of the events they contain.
- **Date positioning** Events are positioned by their assigned resource and exact date range.
- **Event interaction** Events highlight on hover and show drag and resize handles for repositioning or changing duration.
- **Event selection** Clicking an event selects and highlights it.
- **Event creation** New events can be created by double-clicking the timeline or by clicking and dragging across a time range.
- **Scrolling** The timeline supports horizontal and vertical scrolling for navigating through time and resources.

## Best for

- **Bulk schedule updates** Selecting several events and applying the same change, such as updating the color or status of multiple scheduled items at once.
- **Resource planning workflows** Managing schedules where events are grouped by resource and users need to act on several assignments in the same timeline view.
- **Operational cleanup** Reviewing a week of visible events and clearing, updating, or deleting selected items without editing each event individually.
- **Multi-event review** Tracking the current selection in a side panel so users can confirm which events will be affected before running a bulk action.
- **Context-specific actions** Giving users quick access to update and delete actions from a right-click menu while still supporting toolbar-style bulk actions.

## 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
- [`getEvents`](https://mobiscroll.com/docs/vue/eventcalendar/api#method-getEvents) - method
- [`data`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-data) - option
- [`resources`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-resources) - option
- [`view`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-view) - option
- [`clickToCreate`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-clickToCreate) - option
- [`selectMultipleEvents`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-selectMultipleEvents) - option
- [`selectedEvents`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-selectedEvents) - option
- [`event-delete`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventDelete) - event
- [`page-loading`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onPageLoading) - event
- [`selected-events-change`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onSelectedEventsChange) - event
- [`event-right-click`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventRightClick) - event
- [`display`](https://mobiscroll.com/docs/vue/select/api#opt-display) - option
- [`inputProps`](https://mobiscroll.com/docs/vue/select/api#opt-inputProps) - option
- [`touchUi`](https://mobiscroll.com/docs/vue/select/api#opt-touchUi) - option
- [`anchor`](https://mobiscroll.com/docs/vue/select/api#opt-anchor) - option
- [`data`](https://mobiscroll.com/docs/vue/select/api#opt-data) - option
- [`isOpen`](https://mobiscroll.com/docs/vue/select/api#opt-isOpen) - option
- [`change`](https://mobiscroll.com/docs/vue/select/api#event-onChange) - event
- [`close`](https://mobiscroll.com/docs/vue/select/api#event-onClose) - event
- [`close`](https://mobiscroll.com/docs/vue/notifications/confirm#event-onClose) - event
- [`close`](https://mobiscroll.com/docs/vue/notifications/toast#event-onClose) - event

## Code

### SFC JS

```vue
<script setup>
import {
  formatDate,
  getJson,
  MbscButton,
  MbscConfirm,
  MbscEventcalendar,
  MbscPage,
  MbscSelect,
  MbscToast,
  setOptions
} from '@mobiscroll/vue'
import { onMounted, ref } from 'vue'

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

const myView = {
  timeline: {
    type: 'week'
  }
}

const myResources = ref([
  {
    id: 1,
    name: 'Ryan',
    color: '#fdf500'
  },
  {
    id: 2,
    name: 'Kate',
    color: '#ff4600'
  },
  {
    id: 3,
    name: 'John',
    color: '#ff0101'
  },
  {
    id: 4,
    name: 'Mark',
    color: '#239a21'
  },
  {
    id: 5,
    name: 'Sharon',
    color: '#8f1ed6'
  },
  {
    id: 6,
    name: 'Ashley',
    color: '#01adff'
  }
])

const contextMenu = [
  {
    text: 'Update',
    value: 'update'
  },
  {
    text: 'Delete',
    value: 'delete'
  }
]

const myEvents = ref([])
const mySelectedEvents = ref([])
const eventTitles = ref()
const firstDay = ref()
const lastDay = ref()
const menuAnchor = ref()
const menuOpen = ref()
const selected = ref()
const isConfirmOpen = ref()
const confirmMessage = ref()
const confirmCallback = ref()
const toastMessage = ref()
const isToastOpen = ref()
const calElm = ref()

function getSelectedEventTitles(events) {
  let titles = []

  for (const event of events) {
    titles = [...titles, event.title]
  }
  return titles
}

function refreshSelectedEvents(events) {
  mySelectedEvents.value = events
  eventTitles.value = getSelectedEventTitles(events)
}

function deleteSelectedEvents() {
  isConfirmOpen.value = true
  confirmMessage.value = getSelectedEventTitles(mySelectedEvents.value).join(', ')
  confirmCallback.value = function (result) {
    if (result) {
      let eventsToUpdate = [...myEvents.value]

      for (const event of mySelectedEvents.value) {
        if (event.recurring) {
          const origEvent = event.original
          let exc = origEvent.recurringException || []
          exc = [...exc, event.start]
          origEvent.recurringException = exc

          // Update the event in the list
          const index = eventsToUpdate.findIndex((x) => x.id === origEvent.id)
          eventsToUpdate.splice(index, 1, origEvent)
        } else {
          eventsToUpdate = eventsToUpdate.filter((ev) => ev.id !== event.id)
        }
      }

      myEvents.value = eventsToUpdate
      refreshSelectedEvents([])

      toastMessage.value = 'Deleted'
      isToastOpen.value = true
    }
    isConfirmOpen.value = false
  }
}

function updateSelectedEvents() {
  const events =
    mySelectedEvents.value.length === 0 ? [mySelectedEvents.value] : mySelectedEvents.value
  let eventsToUpdate = [...myEvents.value]

  for (const event of events) {
    if (event.recurring) {
      const origEvent = event.original
      let exc = origEvent.recurringException || []
      const newEvent = event

      newEvent.recurring = undefined
      newEvent.color = 'orange'
      newEvent.id += '_' + formatDate('YYYY-MM-DD', event.start)
      eventsToUpdate = [...eventsToUpdate, newEvent]

      exc = [...exc, event.start]
      origEvent.recurringException = exc

      // Update the event in the list
      const index = eventsToUpdate.findIndex((x) => x.id === origEvent.id)
      eventsToUpdate.splice(index, 1, origEvent)
    } else {
      const newEv = event
      newEv.color = 'orange'
      const index = eventsToUpdate.findIndex((x) => x.id === newEv.id)
      eventsToUpdate.splice(index, 1, newEv)
    }
  }

  toastMessage.value = "All selected event's color changed to orange"
  isToastOpen.value = true

  myEvents.value = eventsToUpdate
  refreshSelectedEvents([])
}

function selectAllEvents() {
  const selectedEvents = calElm.value.instance.getEvents(firstDay.value, lastDay.value)
  refreshSelectedEvents(selectedEvents)
  toastMessage.value = 'All events selected from view'
  isToastOpen.value = true
}

function resetSelection() {
  refreshSelectedEvents([])
  toastMessage.value = 'Selection cleared'
  isToastOpen.value = true
}

function handlePageLoading(args) {
  setTimeout(() => {
    firstDay.value = args.firstDay
    lastDay.value = args.lastDay
  })
}

function handleSelectedEventsChange(args) {
  if (args.events) {
    refreshSelectedEvents(args.events)
  }
}

function handleEventRightClick(args) {
  args.domEvent.preventDefault()
  menuAnchor.value = args.domEvent.target
  setTimeout(() => {
    menuOpen.value = true
  })
}

function handleEventDelete() {
  if (!isConfirmOpen.value) {
    deleteSelectedEvents()
    return false
  }
}

function handleSelectChange(args) {
  selected.value = args.value
  if (args.value === 'update') {
    updateSelectedEvents()
  } else if (args.value === 'delete') {
    deleteSelectedEvents()
  }
}

function handleMenuClose() {
  selected.value = ''
  menuOpen.value = false
}

function handleConfirmClose() {
  isConfirmOpen.value = false
}

function handleToastClose() {
  isToastOpen.value = false
}

onMounted(() => {
  getJson(
    'https://trial.mobiscroll.com/timeline-events/',
    (events) => {
      myEvents.value = events
    },
    'jsonp'
  )

  document.querySelector('.md-bulk-operations').addEventListener('keydown', (ev) => {
    if (!isConfirmOpen.value && (ev.keyCode === 8 || ev.keyCode === 46)) {
      deleteSelectedEvents()
    }
  })
})
</script>

<template>
  <MbscPage className="md-bulk-operations">
    <div class="mbsc-grid mbsc-no-padding">
      <div class="mbsc-row">
        <div class="mbsc-col-sm-9 mbsc-push-sm-3">
          <MbscEventcalendar
            className="md-bulk-operations-border"
            ref="calElm"
            :data="myEvents"
            :resources="myResources"
            :view="myView"
            :clickToCreate="true"
            :selectMultipleEvents="true"
            :selectedEvents="mySelectedEvents"
            @event-delete="handleEventDelete"
            @page-loading="handlePageLoading"
            @selected-events-change="handleSelectedEventsChange"
            @event-right-click="handleEventRightClick"
          />
          <MbscSelect
            display="anchored"
            :inputProps="{ type: 'hidden' }"
            :touchUi="false"
            :anchor="menuAnchor"
            :data="contextMenu"
            :value="selected"
            :isOpen="menuOpen"
            @change="handleSelectChange"
            @close="handleMenuClose"
          />
        </div>
        <div class="mbsc-col-sm-3 mbsc-pull-sm-9">
          <div class="mbsc-form-group">
            <div class="mbsc-button-group-block">
              <MbscButton @click="selectAllEvents">Select all this month</MbscButton>
              <MbscButton @click="resetSelection">Reset selection</MbscButton>
              <MbscButton @click="updateSelectedEvents">Update selected</MbscButton>
            </div>
          </div>
          <div class="mbsc-form-group-title">Currently selected</div>
          <div class="mbsc-padding md-selected-event-list">
            <ul>
              <li v-for="title in eventTitles" :key="title">{{ title }}</li>
            </ul>
          </div>
        </div>
      </div>
    </div>
    <MbscConfirm
      title="Are you sure you want to delete the following events?"
      okText="Delete"
      :message="confirmMessage"
      :callback="confirmCallback"
      :isOpen="isConfirmOpen"
      @close="handleConfirmClose"
    />
    <MbscToast :message="toastMessage" :isOpen="isToastOpen" @close="handleToastClose" />
  </MbscPage>
</template>

<style>
.md-bulk-operations-border {
  border-left: 1px solid #ccc;
}

.md-selected-event-list {
  height: 500px;
  overflow: auto;
  padding-top: 0;
  padding-bottom: 0;
}
</style>
```

### SFC TS

```vue
<script setup lang="ts">
import {
  formatDate,
  getJson,
  MbscButton,
  MbscConfirm,
  MbscEventcalendar,
  MbscPage,
  MbscSelect,
  MbscToast,
  setOptions
} from '@mobiscroll/vue'
import type {
  MbscCalendarEvent,
  MbscEventcalendarView,
  MbscEventClickEvent,
  MbscPageLoadingEvent,
  MbscResource,
  MbscSelectedEventsChangeEvent
} from '@mobiscroll/vue'
import { onMounted, ref } from 'vue'

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

const myView: MbscEventcalendarView = {
  timeline: {
    type: 'week'
  }
}

const myResources = ref<MbscResource[]>([
  {
    id: 1,
    name: 'Ryan',
    color: '#fdf500'
  },
  {
    id: 2,
    name: 'Kate',
    color: '#ff4600'
  },
  {
    id: 3,
    name: 'John',
    color: '#ff0101'
  },
  {
    id: 4,
    name: 'Mark',
    color: '#239a21'
  },
  {
    id: 5,
    name: 'Sharon',
    color: '#8f1ed6'
  },
  {
    id: 6,
    name: 'Ashley',
    color: '#01adff'
  }
])

const contextMenu = [
  {
    text: 'Update',
    value: 'update'
  },
  {
    text: 'Delete',
    value: 'delete'
  }
]

const myEvents = ref<MbscCalendarEvent[]>([])
const mySelectedEvents = ref<MbscCalendarEvent[]>([])
const eventTitles = ref<string[]>([])
const firstDay = ref<any>(null)
const lastDay = ref<any>(null)
const menuAnchor = ref<any>(null)
const menuOpen = ref<boolean>(false)
const selected = ref<string>('')
const isConfirmOpen = ref<boolean>(false)
const confirmMessage = ref<string>('')
const confirmCallback = ref<any>()
const toastMessage = ref<string>('')
const isToastOpen = ref<boolean>(false)
const calElm = ref<any>()

function getSelectedEventTitles(events: MbscCalendarEvent[]) {
  return events.map((e) => e.title || '')
}

function refreshSelectedEvents(events: MbscCalendarEvent[]) {
  mySelectedEvents.value = events
  eventTitles.value = getSelectedEventTitles(events)
}

function deleteSelectedEvents() {
  isConfirmOpen.value = true
  confirmMessage.value = getSelectedEventTitles(mySelectedEvents.value).join(', ')
  confirmCallback.value = (result: boolean) => {
    if (result) {
      let eventsToUpdate = [...myEvents.value]

      for (const event of mySelectedEvents.value) {
        if (event.recurring) {
          const origEvent = event.original!
          let exc: any = origEvent.recurringException || []
          exc = [...exc, event.start]
          origEvent.recurringException = exc

          // Update the event in the list
          const index = eventsToUpdate.findIndex((x) => x.id === origEvent.id)
          eventsToUpdate.splice(index, 1, origEvent)
        } else {
          eventsToUpdate = eventsToUpdate.filter((ev) => ev.id !== event.id)
        }
      }

      myEvents.value = eventsToUpdate
      refreshSelectedEvents([])

      toastMessage.value = 'Deleted'
      isToastOpen.value = true
    }
    isConfirmOpen.value = false
  }
}

function updateSelectedEvents() {
  const events: any =
    mySelectedEvents.value.length === 0 ? [mySelectedEvents.value] : mySelectedEvents.value
  let eventsToUpdate = [...myEvents.value]

  for (const event of events) {
    if (event.recurring) {
      const origEvent = event.original
      let exc = origEvent.recurringException || []
      const newEvent = event

      newEvent.recurring = undefined
      newEvent.color = 'orange'
      newEvent.id += '_' + formatDate('YYYY-MM-DD', event.start)
      eventsToUpdate = [...eventsToUpdate, newEvent]

      exc = [...exc, event.start]
      origEvent.recurringException = exc

      // Update the event in the list
      const index = eventsToUpdate.findIndex((x) => x.id === origEvent.id)
      eventsToUpdate.splice(index, 1, origEvent)
    } else {
      const newEv = event
      newEv.color = 'orange'
      const index = eventsToUpdate.findIndex((x) => x.id === newEv.id)
      eventsToUpdate.splice(index, 1, newEv)
    }
  }

  toastMessage.value = "All selected event's color changed to orange"
  isToastOpen.value = true

  myEvents.value = eventsToUpdate
  refreshSelectedEvents([])
}

function selectAllEvents() {
  const selectedEvents = calElm.value.instance.getEvents(firstDay.value, lastDay.value)
  refreshSelectedEvents(selectedEvents)
  toastMessage.value = 'All events selected from view'
  isToastOpen.value = true
}

function resetSelection() {
  refreshSelectedEvents([])
  toastMessage.value = 'Selection cleared'
  isToastOpen.value = true
}

function handlePageLoading(args: MbscPageLoadingEvent) {
  setTimeout(() => {
    firstDay.value = args.firstDay
    lastDay.value = args.lastDay
  })
}

function handleSelectedEventsChange(args: MbscSelectedEventsChangeEvent) {
  if (args.events) {
    refreshSelectedEvents(args.events)
  }
}

function handleEventRightClick(args: MbscEventClickEvent) {
  args.domEvent.preventDefault()
  menuAnchor.value = args.domEvent.target
  setTimeout(() => {
    menuOpen.value = true
  })
}

function handleEventDelete() {
  if (!isConfirmOpen.value) {
    deleteSelectedEvents()
    return false
  }
}

function handleSelectChange(args: any) {
  selected.value = args.value
  if (args.value === 'update') {
    updateSelectedEvents()
  } else if (args.value === 'delete') {
    deleteSelectedEvents()
  }
}

function handleMenuClose() {
  selected.value = ''
  menuOpen.value = false
}

function handleConfirmClose() {
  isConfirmOpen.value = false
}

function handleToastClose() {
  isToastOpen.value = false
}

onMounted(() => {
  getJson(
    'https://trial.mobiscroll.com/timeline-events/',
    (events) => {
      myEvents.value = events
    },
    'jsonp'
  )

  document.querySelector('.md-bulk-operations')!.addEventListener('keydown', (ev: any) => {
    if (!isConfirmOpen.value && (ev.keyCode === 8 || ev.keyCode === 46)) {
      deleteSelectedEvents()
    }
  })
})
</script>

<template>
  <MbscPage className="md-bulk-operations">
    <div class="mbsc-grid mbsc-no-padding">
      <div class="mbsc-row">
        <div class="mbsc-col-sm-9 mbsc-push-sm-3">
          <MbscEventcalendar
            className="md-bulk-operations-border"
            ref="calElm"
            :data="myEvents"
            :resources="myResources"
            :view="myView"
            :clickToCreate="true"
            :selectMultipleEvents="true"
            :selectedEvents="mySelectedEvents"
            @event-delete="handleEventDelete"
            @page-loading="handlePageLoading"
            @selected-events-change="handleSelectedEventsChange"
            @event-right-click="handleEventRightClick"
          />
          <MbscSelect
            display="anchored"
            :inputProps="{ type: 'hidden' }"
            :touchUi="false"
            :anchor="menuAnchor"
            :data="contextMenu"
            :value="selected"
            :isOpen="menuOpen"
            @change="handleSelectChange"
            @close="handleMenuClose"
          />
        </div>
        <div class="mbsc-col-sm-3 mbsc-pull-sm-9">
          <div class="mbsc-form-group">
            <div class="mbsc-button-group-block">
              <MbscButton @click="selectAllEvents">Select all this month</MbscButton>
              <MbscButton @click="resetSelection">Reset selection</MbscButton>
              <MbscButton @click="updateSelectedEvents">Update selected</MbscButton>
            </div>
          </div>
          <div class="mbsc-form-group-title">Currently selected</div>
          <div class="mbsc-padding md-selected-event-list">
            <ul>
              <li v-for="title in eventTitles" :key="title">{{ title }}</li>
            </ul>
          </div>
        </div>
      </div>
    </div>
    <MbscConfirm
      title="Are you sure you want to delete the following events?"
      okText="Delete"
      :message="confirmMessage"
      :callback="confirmCallback"
      :isOpen="isConfirmOpen"
      @close="handleConfirmClose"
    />
    <MbscToast :message="toastMessage" :isOpen="isToastOpen" @close="handleToastClose" />
  </MbscPage>
</template>

<style>
.md-bulk-operations-border {
  border-left: 1px solid #ccc;
}

.md-selected-event-list {
  height: 500px;
  overflow: auto;
  padding-top: 0;
  padding-bottom: 0;
}
</style>
```

## Implementation notes

- Use `timeline: { type: 'week' }` - a weekly view.
- Set `selectMultipleEvents: true` to enable Ctrl/Shift/Cmd+click multi-selection.
- Define 6 named resources (Ryan, Kate, John, Mark, Sharon, Ashley) each with `id`, `name`, and `color`.
- Load events asynchronously from `https://trial.mobiscroll.com/timeline-events/` via JSONP; set them on the calendar after load.
- Use a two-column layout: the calendar occupies 9/12 columns (pushed right), the sidebar 3/12 (pulled left). The sidebar contains three action buttons ("Select all from view", "Reset selection", "Update selected") and a scrollable list of currently selected event titles.
- Track `firstDay` and `lastDay` via `onPageLoading` (`args.firstDay` / `args.lastDay`). These are used by "Select all from view".
- `onSelectedEventsChange` → update the current selection and refresh the displayed titles list. React/Angular/Vue: the selection is tracked via the bound `selectedEvents` prop/binding (two-way); this handler just mirrors it into local state for the titles list.
- **"Select all from view"** → call `calendar.getEvents(firstDay, lastDay)` to get the visible events, then update the selection and the titles list. React/Angular/Vue: assign the result to the state/binding feeding the `selectedEvents` prop. JS/jQuery: pass the result to `calendar.setSelectedEvents()`. Shows toast "All events selected from view".
- **"Reset selection"** → clear the selection and the titles list. React/Angular/Vue: set the `selectedEvents` state/binding to `[]`. JS/jQuery: call `calendar.setSelectedEvents([])`. Shows toast "Selection cleared".
- **"Update selected"** → `updateSelectedEvents()`: get the currently selected events - React/Angular/Vue: read them from the bound `selectedEvents` state/binding; JS/jQuery: call `calendar.getSelectedEvents()` - then iterate and change color to orange. For **recurring occurrences**: detach the occurrence - set `recurring = undefined`, assign a new `id` by appending `_` + `formatDate('YYYY-MM-DD', event.start)`, add the occurrence's start to `origEvent.recurringException`. React/Angular/Vue: replace `origEvent` in place and append `newEvent` in the array bound to `data`. JS/jQuery: call `calendar.updateEvent(origEvent)` and `calendar.addEvent(newEvent)`. For **non-recurring**: set `color = 'orange'`. React/Angular/Vue: replace the event in place in the array bound to `data`. JS/jQuery: call `calendar.updateEvent(event)`. After: clear the selection (React/Angular/Vue: set the `selectedEvents` state/binding to `[]`; JS/jQuery: call `calendar.setSelectedEvents([])`), show toast "All selected event's color changed to orange".
- **`deleteSelectedEvents()`** → set `confirmOpen = true`, show a confirm dialog ("Are you sure you want to delete the following events?") with the selected event titles joined as the message and "Delete" as the ok button. On confirm: for **recurring** events, push `event.start` into `origEvent.recurringException` - React/Angular/Vue: replace `origEvent` in place in the array bound to `data`; JS/jQuery: call `calendar.updateEvent(origEvent)`. For **non-recurring**, collect them, then remove them - React/Angular/Vue: filter `eventsToDelete` out of the array bound to `data`; JS/jQuery: call `calendar.removeEvent(eventsToDelete)`. After: clear the selection (React/Angular/Vue: set the `selectedEvents` state/binding to `[]`; JS/jQuery: call `calendar.setSelectedEvents([])`), show toast "Deleted", set `confirmOpen = false`.
- Three entry points trigger `deleteSelectedEvents()`: (1) **`onEventDelete`** - fired by the Delete/Backspace key; use the `confirmOpen` flag to prevent double-triggering, then `return false`; (2) **`onEventUpdate` with `args.isDelete === true`** - fired when Mobiscroll attempts a keyboard delete on a multi-selection; same guard and `return false`; (3) **context menu "Delete" option**.
- **Right-click context menu**: `onEventRightClick` → store `selectedEvent = args.event` as a fallback (used if no events are selected), `args.domEvent.preventDefault()`, set the menu anchor to `args.domEvent.target`, open an anchored `Select` popup (`display: 'anchored'`, `touchUi: false`, no buttons) with two options ("Update" / "Delete"). On selection dispatch to `updateSelectedEvents()` or `deleteSelectedEvents()`; `updateSelectedEvents` / `deleteSelectedEvents` fall back to `[selectedEvent]` when the current selection is empty (React/Angular/Vue: the bound `selectedEvents` state/binding; JS/jQuery: `getSelectedEvents()`). On menu close, clear the selected value.
- **Keyboard delete**: attach a `keydown` listener on the `.md-bulk-operations` container; if Delete or Backspace is pressed and no confirm dialog is open, call `deleteSelectedEvents()`.

## 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: [Block Past Event Creation](https://demo.mobiscroll.com/vue/timeline/disallow-past-event-creation.md) - Keep past dates read-only on a Vue resource timeline — block creation, moving, and resizing, and show a toast for blocked actions.
