Angular Event calendar Cut, Copy, Paste Events
Get started with this exampleUsage
theme: 'ios' You can turn auto theming on and let it decide based on the platform.
Usage
themeVariant: 'light' You can turn auto dark mode on based on system settings.
Usage
clickToCreate: true,
// click to create is on dragToCreate: true,
// drag to create is on dragToMove: true,
// reordering is on dragToResize: true,
// resizing is on eventDelete: true
// deleting is on Fine-tune drag & drop with these options, which are false by default.
Usage
locale: localeEn Selecting the locale loads the localized settings and the translated UI text.
Cut, Copy, Paste Events
While copy & pasting one or more events is useful, sometimes being able to move events between two separate calendars can improve productivity. How the calendars are laid out depends on the application, but the basic idea is that you will need to be able to tell where the events from the clipboard will be pasted.
In this example this is implemented by tracking the active instance, which is determined by the active tab of the segmented control. With this out of the way, CTRL/CMD+C/X/Z/V handle the copy, cut, undo and paste actions.
Demo

Code
import { CommonModule } from '@angular/common';
import { Component, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import {
MbscCalendarEvent,
MbscEventcalendarOptions,
MbscModule,
MbscSelectOptions,
Notifications,
setOptions ,
} from '@mobiscroll/angular';
setOptions({
theme: ' ',
themeVariant: ' ',
//locale
});
const today = new Date();
const menuData = [
{ text: 'Copy', value: 'c' },
{ text: 'Cut', value: 'x' },
{ text: 'Paste', value: 'v' },
{ text: 'Delete', value: 'delete' },
];
const disabledMenu = [
{ text: 'Copy', value: 'c', disabled: true },
{ text: 'Cut', value: 'x', disabled: true },
{ text: 'Paste', value: 'v' },
{ text: 'Delete', value: 'delete', disabled: true },
];
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
providers: [Notifications],
standalone: true,
imports: [CommonModule, FormsModule, MbscModule],
})
export class AppComponent {
constructor(private notify: Notifications) {}
@ViewChild('menu', { static: false })
menu!: any;
@ViewChild('dummy', { static: false })
dummy!: any;
firstEvents: MbscCalendarEvent[] = [
{
start: '2026-11-02T09:00',
end: '2026-11-06T18:00',
title: 'Business of Software Conference',
color: '#ff6d42',
},
{
start: '2026-11-10T07:00',
end: '2026-11-10T08:00',
title: 'Green box to post office',
color: '#6e7f29',
},
{
start: '2026-11-15T09:30',
end: '2026-11-15T10:30',
title: 'Product team mtg.',
color: '#f67944',
},
{
start: '2026-11-23T11:00',
end: '2026-11-23T11:45',
title: 'Stakeholder mtg.',
color: '#a144f6',
},
{
start: '2026-11-28T13:00',
end: '2026-11-28T13:45',
title: "Lunch @ Butcher's",
color: '#00aabb',
},
];
secondEvents: MbscCalendarEvent[] = [
{
start: '2026-11-04T08:45',
end: '2026-11-04T10:00',
title: 'Quick mtg. with Martin',
color: '#de3d83',
},
{
start: '2026-11-08T15:00',
end: '2026-11-08T16:00',
title: 'General orientation',
color: '#a71111',
},
{
start: '2026-11-10T13:00',
end: '2026-11-14T21:00',
title: 'Friends binge marathon',
color: '#7bde83',
},
{
start: '2026-11-23T08:00',
end: '2026-11-27T09:00',
title: 'Product team mtg.',
color: '#913aa7',
},
];
menuData = menuData;
selectValue: any;
menuAnchor: any;
activeCalendar = 'first';
cutCalendar = 'first';
toDate: any = today;
firstToDate: Date | undefined = today;
secondToDate: Date | undefined = today;
originDate: any = today;
menuOpen = false;
firstSelectedEvents: MbscCalendarEvent[] = [];
secondSelectedEvents: MbscCalendarEvent[] = [];
selectedEvents: MbscCalendarEvent[] = [];
moveEvents: MbscCalendarEvent[] = [];
pastedEvents: MbscCalendarEvent[] = [];
deletedEvents: MbscCalendarEvent[] = [];
action: string | undefined;
calendarSettings: MbscEventcalendarOptions = {
clickToCreate: true,
dragToCreate: true,
dragToMove: true,
dragToResize: true,
selectMultipleEvents: true,
view: {
calendar: {
labels: 'all',
},
},
onPageLoading: (args) => {
if (this.activeCalendar === 'first') {
this.firstToDate = args.month;
} else {
this.secondToDate = args.month;
}
this.toDate = args.month;
},
onCellRightClick: (args) => {
if (!this.menuOpen) {
args.domEvent.preventDefault();
this.menuData = disabledMenu;
this.menuAnchor = args.domEvent.target;
setTimeout(() => {
this.menu.open();
});
}
},
onEventRightClick: (args) => {
const activeEvents = this.activeCalendar === 'first' ? this.firstEvents : this.secondEvents;
if (activeEvents.length <= 1) {
if (this.activeCalendar === 'first') {
this.firstEvents = [args.event];
} else {
this.secondEvents = [args.event];
}
}
args.domEvent.preventDefault();
this.menuData = menuData;
this.menuAnchor = args.domEvent.target;
setTimeout(() => {
this.menu.open();
});
this.menuOpen = true;
},
onEventDeleted: (args: any) => {
this.deletedEvents = args.events;
this.action = 'delete';
this.notify.snackbar({
button: {
action: () => {
const activeEvents = this.activeCalendar === 'first' ? this.firstEvents : this.secondEvents;
let eventsToUpdate = [...activeEvents];
for (const event of this.deletedEvents) {
eventsToUpdate = eventsToUpdate.filter((ev) => ev.id !== event.id);
}
if (this.activeCalendar === 'first') {
this.firstEvents = eventsToUpdate;
} else {
this.secondEvents = eventsToUpdate;
}
this.deletedEvents = [];
},
text: 'Undo',
},
duration: 3000,
message: 'Event' + (this.deletedEvents.length === 1 ? '' : 's') + ' deleted',
});
this.dummy.nativeElement.focus();
},
};
monthDiff(d1: Date, d2: Date) {
return d2.getMonth() - d1.getMonth() + 12 * (d2.getFullYear() - d1.getFullYear());
}
pasteEvents() {
const activeEvents: any = this.getActiveEvents();
const activeSelectedEvents: any = this.selectedEvents;
let eventsToUpdate = [...activeEvents];
if (activeSelectedEvents.length > 0) {
for (const event of activeSelectedEvents) {
const newEvent = Object.assign({}, event);
const startDate: any = new Date(event.start);
const endDate: any = new Date(event.end);
const diff = Math.abs(endDate - startDate);
newEvent.start = startDate.setMonth(startDate.getMonth() - this.monthDiff(this.toDate, this.originDate));
newEvent.end = new Date(startDate.getTime() + diff);
delete newEvent.id;
eventsToUpdate = [...eventsToUpdate, newEvent];
this.pastedEvents = [...this.pastedEvents, newEvent];
}
if (this.activeCalendar === 'first') {
this.firstEvents = eventsToUpdate;
} else {
this.secondEvents = eventsToUpdate;
}
if (this.action === 'cut') {
let cutEvs = this.cutCalendar === 'first' ? this.firstEvents : this.secondEvents;
this.moveEvents = [...this.selectedEvents];
for (const event of this.selectedEvents) {
cutEvs = cutEvs.filter((ev) => ev.id !== event.id);
}
if (this.cutCalendar === 'first') {
this.firstEvents = cutEvs;
} else {
this.secondEvents = cutEvs;
}
this.notify.snackbar({
button: {
action: () => {
let revertEvs = this.cutCalendar === 'first' ? this.firstEvents : this.secondEvents;
for (const event of this.moveEvents) {
revertEvs = [...revertEvs, event];
}
if (this.cutCalendar === 'first') {
this.firstEvents = revertEvs;
} else {
this.secondEvents = revertEvs;
}
let cutEvs = this.getActiveEvents();
for (const event of this.pastedEvents) {
cutEvs = cutEvs.filter((ev) => ev.id !== event.id);
}
if (this.activeCalendar === 'first') {
this.firstEvents = cutEvs;
} else {
this.secondEvents = cutEvs;
}
this.notify.toast({
message: 'Event' + (this.selectedEvents.length === 1 ? '' : 's') + ' reverted',
});
},
text: 'Undo',
},
duration: 3000,
message: 'Event' + (this.selectedEvents.length === 1 ? '' : 's') + ' pasted',
});
this.dummy.nativeElement.focus();
} else {
this.notify.toast({
message: 'Event' + (activeSelectedEvents.length === 1 ? '' : 's') + ' pasted',
});
}
if (this.action !== 'copy') {
this.selectedEvents = [];
}
}
}
getActiveEvents() {
return this.activeCalendar === 'first' ? this.firstEvents : this.secondEvents;
}
getActiveSelectedEvents() {
return this.activeCalendar === 'first' ? this.firstSelectedEvents : this.secondSelectedEvents;
}
deleteEvents() {
const activeEvents = this.getActiveEvents();
let eventsToUpdate = [...activeEvents];
this.action = 'delete';
const activeSelectedEvents = this.getActiveSelectedEvents();
if (activeSelectedEvents.length > 0) {
this.deletedEvents = activeSelectedEvents;
for (const event of activeSelectedEvents) {
eventsToUpdate = eventsToUpdate.filter((ev) => ev.id !== event.id);
}
if (this.activeCalendar === 'first') {
this.firstEvents = eventsToUpdate;
} else {
this.secondEvents = eventsToUpdate;
}
this.notify.snackbar({
button: {
action: () => {
for (const event of activeSelectedEvents) {
eventsToUpdate = [...eventsToUpdate, event];
}
if (this.activeCalendar === 'first') {
this.firstEvents = eventsToUpdate;
} else {
this.secondEvents = eventsToUpdate;
}
this.deletedEvents = [];
},
text: 'Undo',
},
duration: 3000,
message: 'Event' + (activeSelectedEvents.length === 1 ? '' : 's') + ' deleted',
});
this.dummy.nativeElement.focus();
}
}
activateAction(type: string) {
if (this.selectedEvents.length > 0) {
const act = type == 'copy' ? ' copied' : ' cut';
this.originDate = this.toDate;
this.notify.toast({
message: 'Event' + (this.selectedEvents.length === 1 ? '' : 's') + act,
});
}
}
copyEvents() {
if (this.activeCalendar === 'first') {
if (this.firstSelectedEvents.length > 0) {
this.action = 'copy';
this.selectedEvents = this.firstSelectedEvents;
this.activateAction('copy');
}
} else {
if (this.secondSelectedEvents.length > 0) {
this.action = 'copy';
this.selectedEvents = this.secondSelectedEvents;
this.activateAction('copy');
}
}
}
cutEvents() {
if (this.activeCalendar === 'first') {
if (this.firstSelectedEvents.length > 0) {
this.action = 'cut';
this.selectedEvents = this.firstSelectedEvents;
this.cutCalendar = this.activeCalendar;
this.activateAction('cut');
this.deletedEvents = [];
}
} else {
if (this.secondSelectedEvents.length > 0) {
this.action = 'cut';
this.selectedEvents = this.secondSelectedEvents;
this.cutCalendar = this.activeCalendar;
this.activateAction('cut');
this.deletedEvents = [];
}
}
}
undoEvents() {
const activeEvents = this.getActiveEvents();
let eventsToUpdate = [...activeEvents];
if (this.action === 'delete') {
for (const event of this.deletedEvents) {
eventsToUpdate = [...eventsToUpdate, event];
}
if (this.activeCalendar === 'first') {
this.firstEvents = eventsToUpdate;
} else {
this.secondEvents = eventsToUpdate;
}
this.deletedEvents = [];
} else if (this.action === 'cut') {
let revertEvs = this.cutCalendar === 'first' ? this.firstEvents : this.secondEvents;
for (const event of this.moveEvents) {
revertEvs = [...revertEvs, event];
}
if (this.cutCalendar === 'first') {
this.firstEvents = revertEvs;
} else {
this.secondEvents = revertEvs;
}
let cutEvs = this.getActiveEvents();
for (const event of this.pastedEvents) {
cutEvs = cutEvs.filter((ev) => ev.id !== event.id);
}
if (this.activeCalendar === 'first') {
this.firstEvents = cutEvs;
} else {
this.secondEvents = cutEvs;
}
this.cutEvents();
this.pastedEvents = [];
}
}
detectAction(key: string) {
switch (key) {
case 'delete': // Delete
this.deleteEvents();
break;
case 'c': // Copy
this.copyEvents();
break;
case 'x': // Cut
this.cutEvents();
break;
case 'z': // Undo
this.undoEvents();
break;
case 'v': // Paste
this.pasteEvents();
break;
}
}
menuSettings: MbscSelectOptions = {
touchUi: false,
showInput: false,
display: 'anchored',
buttons: [],
onChange: (args: any) => {
this.detectAction(args.value);
},
onClose: () => {
this.menuOpen = false;
// Clear selection
setTimeout(() => {
this.selectValue = null;
});
},
};
switchCalendar(ev: any) {
if (ev.target.value === 'first') {
this.toDate = this.firstToDate;
this.secondSelectedEvents = [];
} else {
this.toDate = this.secondToDate;
this.firstSelectedEvents = [];
}
}
onKeydown(ev: any) {
if (ev.ctrlKey || ev.metaKey) {
this.detectAction(ev.key);
}
if (ev.key === 'Delete') {
this.detectAction('delete');
}
}
}
<mbsc-page (keydown)="onKeydown($event)">
<div class="mbsc-flex-col md-copy-cut-paste">
<div class="mbsc-flex-none">
<mbsc-segmented-group [(ngModel)]="activeCalendar" (change)="switchCalendar($event)">
<mbsc-segmented value="first">First calendar</mbsc-segmented>
<mbsc-segmented value="second">Second calendar</mbsc-segmented>
</mbsc-segmented-group>
</div>
<div class="mbsc-flex-col mbsc-flex-1-1">
<div id="demo-copy-cut-paste-first-cont" class="mbsc-flex-1-1 md-copy-cut-paste-cont">
<mbsc-eventcalendar
[data]="firstEvents"
[(selectedEvents)]="firstSelectedEvents"
[options]="calendarSettings"
[ngClass]="{'md-hide-calendar': activeCalendar === 'second'}"
></mbsc-eventcalendar>
</div>
<div id="demo-copy-cut-paste-second-cont" class="mbsc-flex-1-1 md-copy-cut-paste-cont">
<mbsc-eventcalendar
[data]="secondEvents"
[(selectedEvents)]="secondSelectedEvents"
[options]="calendarSettings"
[ngClass]="{'md-hide-calendar': activeCalendar === 'first'}"
></mbsc-eventcalendar>
</div>
<mbsc-select #menu [(ngModel)]="selectValue" [data]="menuData" [anchor]="menuAnchor" [options]="menuSettings"></mbsc-select>
</div>
</div>
<div #dummy tabindex="-1"></div>
</mbsc-page>
.md-copy-cut-paste .mbsc-segmented {
width: 300px;
margin-left: auto;
margin-right: auto;
}
.md-copy-cut-paste .md-hide-calendar {
visibility: hidden;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}