Copy paste records in React Gantt component

2 Feb 202311 minutes to read

You can copy and paste a record in the Gantt chart by using the addRecord method and custom context menu. It is also possible to copy and paste the parent record with multiple hierarchical child records on the required position.

import React, { useRef } from 'react';
import ReactDOM from 'react-dom';
import {
  GanttComponent,
  Inject,
  Edit,
  Selection,
  ContextMenu,
} from '@syncfusion/ej2-react-gantt';
import { taskData } from './datasource';

function App() {
  const ganttRef = useRef(null);
  let copiedRecord;

  const taskFields = {
    id: 'TaskID',
    name: 'TaskName',
    startDate: 'StartDate',
    duration: 'Duration',
    progress: 'Progress',
    dependency: 'Predecessor',
    parentID: 'ParentId'
  };

  const editSettings = {
    allowAdding: true,
    allowEditing: true,
    allowDeleting: true,
  };

  const contextMenuItems = [
    { text: 'Copy', target: '.e-content', id: 'copy' },
    { text: 'Paste', target: '.e-content', id: 'paste' },
  ];

  const contextMenuClick = (args) => {
    if (args.item.id === 'copy') {
      copiedRecord = args.rowData;
      // Generate a new TaskID
      copiedRecord.taskData.TaskID = ganttRef.current.currentViewData.length + 1;
    }

    if (args.item.id === 'paste' && copiedRecord) {
        ganttRef.current.addRecord(
        copiedRecord.taskData,
        'Below',
        args.rowData?.index
      );
      if (copiedRecord.hasChildRecords) {
        addChildRecords(copiedRecord, args.rowData?.index + 1);
      }
      copiedRecord = undefined; // Clear copied record after pasting
    }
  };

  const contextMenuOpen = (args) => {
    if (args.type !== 'Header') {
      if (copiedRecord) {
        args.hideItems.push('Copy'); // Hide "Copy" if already copied
      } else {
        args.hideItems.push('Paste'); // Hide "Paste" if nothing is copied
      }
    }
  };

  const addChildRecords = (record, index) => {
    record.childRecords.forEach((childRecord, i) => {
      childRecord.taskData.TaskID = ganttRef.current.currentViewData.length + 1;
      ganttRef.current.addRecord(childRecord.taskData, 'Below', index + i);

      if (childRecord.hasChildRecords) {
        addChildRecords(childRecord, index + i + 1);
      }
    });
  };

  return (
    <GanttComponent
      dataSource={taskData}
      taskFields={taskFields}
      editSettings={editSettings}
      enableContextMenu={true}
      contextMenuItems={contextMenuItems}
      contextMenuClick={contextMenuClick}
      contextMenuOpen={contextMenuOpen}
      ref={ganttRef}
      height="450px"
    >
      <Inject services={[Edit, ContextMenu, Selection]} />
    </GanttComponent>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
import React, { useRef } from 'react';
import ReactDOM from 'react-dom';
import { GanttComponent, Inject, Edit, Selection, ContextMenu, Gantt } from '@syncfusion/ej2-react-gantt';
import { taskData } from './datasource';

function App() {
  const ganttRef = useRef<Gantt | null>(null);

  let copiedRecord: any = null;

  const taskFields = {
    id: 'TaskID',
    name: 'TaskName',
    startDate: 'StartDate',
    duration: 'Duration',
    progress: 'Progress',
    dependency: 'Predecessor',
    parentID: 'ParentId'
  };

  const editSettings = {
    allowAdding: true,
    allowEditing: true,
    allowDeleting: true,
  };

  // Context menu items as a plain array
  const contextMenuItems = [
    { text: 'Copy', target: '.e-content', id: 'copy' },
    { text: 'Paste', target: '.e-content', id: 'paste' },
  ];

  const contextMenuClick = (args: any): void => {
    const gantt = ganttRef.current;

    if (!gantt) return;

    if (args.item.id === 'copy') {
      copiedRecord = args.rowData;
      copiedRecord.taskData.TaskID = gantt.currentViewData.length + 1; // Assign unique TaskID
    }

    if (args.item.id === 'paste' && copiedRecord) {
      gantt.addRecord(copiedRecord.taskData, 'Below', args.rowData.index);

      if (copiedRecord.hasChildRecords) {
        addChildRecords(copiedRecord, args.rowData.index + 1);
      }

      copiedRecord = null;
    }
  };

  const contextMenuOpen = (args: any): void => {
    if (args.type !== 'Header') {
      if (copiedRecord) {
        args.hideItems.push('Copy');
      } else {
        args.hideItems.push('Paste');
      }
    }
  };

  const addChildRecords = (record: any, index: number): void => {
    const gantt = ganttRef.current;

    if (!gantt) return;

    record.childRecords.forEach((childRecord: any, i: number) => {
      childRecord.taskData.TaskID = gantt.currentViewData.length + 1; // Assign unique TaskID
      gantt.addRecord(childRecord.taskData, 'Below', index + i);

      if (childRecord.hasChildRecords) {
        addChildRecords(childRecord, index + i + 1);
      }
    });
  };

  return (
    <GanttComponent
      dataSource={taskData}
      taskFields={taskFields}
      editSettings={editSettings}
      enableContextMenu={true}
      contextMenuItems={contextMenuItems}
      contextMenuClick={contextMenuClick}
      contextMenuOpen={contextMenuOpen}
      ref={(gantt) => (ganttRef.current = gantt)}
      height="450px"
    >
      <Inject services={[Edit, ContextMenu, Selection]} />
    </GanttComponent>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React Gantt</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="https://cdn.syncfusion.com/ej2/29.2.4/material.css" rel="stylesheet" type="text/css"/>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
     <style>
        #loader {
            color: #008cff;
            height: 40px;
            left: 45%;
            position: absolute;
            top: 45%;
            width: 30%;
        }
    </style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

<body>
        <div id='root'>
            <div id='loader'>Loading....</div>
        </div>
</body>

</html>