Paste appointment object to Schedule

I'd like to use the copy & paste functionlity provided by the Schedule component. However due to my app running inside Microsoft Teams, direct access to the clipboard for pasting is not possible (copying works though). 

Image_4099_1762613108268

That means I can get the copied value from the clipboard using the Teams SDK but don't know how to then paste it to the schedule. Is there a way around this or could this be logged as a feature request?

Thanks


7 Replies

VR Vijay Ravi Syncfusion Team November 10, 2025 08:54 AM UTC

Hi Thomas Pentenrieder,

Greetings from Syncfusion Support.

Before proceeding, we require additional details regarding your reported query. We have reviewed and validated your initial report, noting that you mentioned your application is running inside Microsoft Teams. Please provide details on how you are running the application, including a video demonstration of the execution and the application file itself, to facilitate further investigation.

Don't hesitate to get in touch if you require further help or more information.

Regards,

Vijay



TP Thomas Pentenrieder November 10, 2025 08:57 AM UTC

Hello, 

the control is running as a Tab app inside Teams, see this video for demo:
https://www.youtube.com/watch?v=SEuS8X0gtUs


This means the app is inside an iframe where I don't have control over the 



VR Vijay Ravi Syncfusion Team November 12, 2025 12:54 PM UTC

Hi Thomas Pentenrieder,,
 

Microsoft Teams apps in iframes face Clipboard API blocks due to Permissions Policy. Developers lack direct <iframe> control to set allow="clipboard-read; clipboard-write". Teams' iframe restrictions prevent standard clipboard access. A workaround involves using a server-side bot to facilitate clipboard actions, relaying content between user and app. This maintains functionality without direct Clipboard API use. Alternate solutions may involve custom messaging extensions, offering streamlined copying.

https://stackoverflow.com/questions/76488175/possibility-to-enable-clipboard-api-in-microsoft-teams-app
https://techcommunity.microsoft.com/discussions/teamsdeveloper/how-can-i-get-clipboard-permission-in-teams-add-on-app/3613731

Don't hesitate to get in touch if you require further help or more information.

Regards,

Vijay



TP Thomas Pentenrieder November 12, 2025 01:03 PM UTC

Please refer to my original question. I am able to get the value from the clipboard using Teams SDK. Even with your suggestion using a bot (?), how can I programatically pass the copied value to the Schedule so it will be correctly inserted into the selected cell / resource?


Also, the linked resources above are out of date.

https://learn.microsoft.com/en-us/javascript/api/@microsoft/teams-js/clipboard?view=msteams-client-js-latest



VR Vijay Ravi Syncfusion Team November 14, 2025 07:44 AM UTC

Hi Thomas Pentenrieder,
 

We reviewed your query about programmatically inserting copied values into the selected cell/resource in the Scheduler. The Scheduler provides built-in methods for cut, copy, and paste operations. You can use these methods to perform cut/copy/paste actions programmatically. Please refer to the documentation linked below for details.

https://ej2.syncfusion.com/react/documentation/schedule/clipboard#cut-copy-and-paste-using-context-menu

Copy API: https://ej2.syncfusion.com/react/documentation/api/schedule/index-default#copy
Cut API: https://ej2.syncfusion.com/react/documentation/api/schedule/index-default#cut
Paste API: https://ej2.syncfusion.com/react/documentation/api/schedule/index-default#paste

Don't hesitate to get in touch if you require further help or more information.

Regards,

Vijay



TP Thomas Pentenrieder November 14, 2025 07:51 AM UTC

No, I cannot use the built-in methods for paste, which is the whole point of this thread. Please look at the error message in post #1.


- I can copy an appointment using the built-in copy method

- I can not paste using the built-in paste method

- I can get the clipboard value programatically through the TeamsJs SDK

- I need a way to pass the copied value to the schedule, e.g. schedule.paste(targetElement, payload), but this is not supported. is there another way?



VR Vijay Ravi Syncfusion Team November 17, 2025 10:18 AM UTC

Hi Thomas Pentenrieder,

We have checked and validated your reported query. The built-in paste relies on clipboard access and won’t work in the Teams iframe. Instead, bypass the clipboard and paste programmatically. Capture the target slot using getCellDetails on the clicked cell (or via cellClick). Take your copied payload (from TeamsJS), normalize its StartTime/EndTime to Date, compute the duration, and create a new event aligned to the target slot, preserving duration and mapping the resource. Use addEvent for “Copy→Paste,” and for “Cut→Paste,” delete the original with deleteEvent before adding the adjusted event. Ensure you generate a unique Id for the pasted copy and carry over flags like IsAllDay/RecurrenceRule. This approach replicates paste behavior without relying on clipboard APIs and works across views/resources.
 

import { createRoot } from 'react-dom/client';

import './index.css';

import * as React from 'react';

import { useRef } from 'react';

import { extend, closest, isNullOrUndefined, remove } from '@syncfusion/ej2-base';

import {

  ScheduleComponent, ViewsDirective, ViewDirective,

  Day, Week, WorkWeek, Month, TimelineViews, TimelineMonth,

  Resize, DragAndDrop, Inject

} from '@syncfusion/ej2-react-schedule';

import { ContextMenuComponent } from '@syncfusion/ej2-react-navigations';

import * as dataSource from './datasource.json';

 

const ClipboardSchedule = () => {

  const scheduleObj = useRef<ScheduleComponent | null>(null);

  const menuObj = useRef<any>(null);

  let selectedTarget: HTMLElement | undefined;

  let targetElement: HTMLElement | undefined;

 

  // Local clipboard (can also be set from TeamsJS payload)

  let clip: any = null;

  let isCut = false;

 

  const data = extend([], (dataSource as any).scheduleData, null, true);

  const menuItems = [

    { text: 'Cut Event', iconCss: 'e-icons e-cut', id: 'Cut' },

    { text: 'Copy Event', iconCss: 'e-icons e-copy', id: 'Copy' },

    { text: 'Paste', iconCss: 'e-icons e-paste', id: 'Paste' }

  ];

 

  // Optional: call this when TeamsJS returns the copied payload

  // Ensure StartTime/EndTime are Date objects

  const setClipboardFromTeams = (payload: any) => {

    clip = {

      ...payload,

      StartTime: new Date(payload.StartTime),

      EndTime: new Date(payload.EndTime)

    };

    isCut = false;

  };

 

  const onContextMenuBeforeOpen = (args: any) => {

    const newEventElement = document.querySelector('.e-new-event');

    if (newEventElement) remove(newEventElement as Element);

 

    scheduleObj.current?.closeQuickInfoPopup();

    targetElement = args.event.target as HTMLElement;

    if (closest(targetElement, '.e-contextmenu')) return;

 

    selectedTarget = closest(

      targetElement,

      '.e-appointment,.e-work-cells,.e-vertical-view .e-date-header-wrap .e-all-day-cells,.e-vertical-view .e-date-header-wrap .e-header-cells'

    ) as HTMLElement;

 

    if (isNullOrUndefined(selectedTarget)) {

      args.cancel = true;

      return;

    }

 

    if (selectedTarget.classList.contains('e-appointment')) {

      menuObj.current.showItems(['Cut', 'Copy'], true);

      menuObj.current.hideItems(['Paste'], true);

    } else {

      menuObj.current.showItems(['Paste'], true);

      menuObj.current.hideItems(['Cut', 'Copy'], true);

    }

  };

 

  const onMenuItemSelect = (args: any) => {

    const id = args.item.id;

    if (!scheduleObj.current) return;

 

    switch (id) {

      case 'Copy': {

        // Get the event object from the clicked appointment element

        const evt = scheduleObj.current.getEventDetails(selectedTarget as Element);

        clip = {

          ...evt,

          StartTime: new Date(evt.StartTime),

          EndTime: new Date(evt.EndTime)

        };

        isCut = false;

        break;

      }

      case 'Cut': {

        const evt = scheduleObj.current.getEventDetails(selectedTarget as Element);

        clip = {

          ...evt,

          StartTime: new Date(evt.StartTime),

          EndTime: new Date(evt.EndTime)

        };

        isCut = true;

        break;

      }

      case 'Paste': {

        if (!clip || !targetElement) return;

 

        // Determine the target cell details (time/resource) from the clicked cell

        const cellEl = closest(targetElement, '.e-work-cells, .e-all-day-cells') as HTMLElement;

        const cell = scheduleObj.current.getCellDetails(cellEl) as any; // { startTime, endTime, resource, groupIndex, ... }

 

        const duration = clip.EndTime.getTime() - clip.StartTime.getTime();

        const newStart = new Date(cell.startTime);

        const newEnd = new Date(newStart.getTime() + duration);

 

        // Map resource field if you use grouping. Adjust 'ResourceId' to your model (e.g., OwnerId).

        const resourceId = cell?.resource?.data?.Id ?? clip.ResourceId;

 

        const adjusted = {

          ...clip,

          Id: Date.now(), // ensure unique Id for copied items

          StartTime: newStart,

          EndTime: newEnd,

          ResourceId: resourceId

        };

 

        if (isCut) {

          scheduleObj.current.deleteEvent(clip); // remove original

          scheduleObj.current.addEvent(adjusted);

          clip = null;

          isCut = false;

        } else {

          scheduleObj.current.addEvent(adjusted);

        }

        break;

      }

    }

  };

 

  return (

    <div className='schedule-control-section'>

      <div className='col-lg-12 control-section'>

        <div className='content-wrapper'>

          <div className='schedule-container'>

            <ScheduleComponent

              width='100%'

              height='550px'

              ref={scheduleObj}

              selectedDate={new Date(2021, 0, 10)}

              eventSettings={{ dataSource: data }}

              allowClipboard={false}  // not using built-in paste

              showQuickInfo={false}

            >

              <ViewsDirective>

                <ViewDirective option='Week' />

                <ViewDirective option='Day' />

                <ViewDirective option='Month' />

                <ViewDirective option='TimelineDay' />

                <ViewDirective option='TimelineWeek' />

                <ViewDirective option='TimelineWorkWeek' />

                <ViewDirective option='TimelineMonth' />

              </ViewsDirective>

              <Inject services={[Day, Week, WorkWeek, Month, TimelineViews, TimelineMonth, Resize, DragAndDrop]} />

            </ScheduleComponent>

 

            <ContextMenuComponent

              target='.e-schedule'

              items={menuItems}

              beforeOpen={onContextMenuBeforeOpen}

              select={onMenuItemSelect}

              cssClass='schedule-context-menu'

              ref={menuObj}

            />

          </div>

        </div>

      </div>

    </div>

  );

};

 

export default ClipboardSchedule;

 

// Mount

const root = createRoot(document.getElementById('sample')!);

root.render(<ClipboardSchedule />);


Don't hesitate to get in touch if you require further help or more information.

Regards,

Vijay


Loader.
Up arrow icon