{
  "$schema": "https://ai.syncfusion.com/schema/registry-item.json",
  "name": "diagram-mind-map",
  "framework": "react",
  "type": "registry:component",
  "component": "Diagram",
  "variant": "mind-map",
  "dependencies": [
    "@syncfusion/ej2-react-diagrams",
    "@syncfusion/ej2-data"
  ],
  "description": "Mind-map diagram with custom user handles to add, branch, and delete nodes.",
  "meta": {
    "essentialStudioRelease": "2026 Volume 2 (v34.1.29)",
    "license": "commercial-or-community",
    "licenseNote": "The Syncfusion package is licensed. The composition in this file is source you own and edit. See https://ai.syncfusion.com/licensing.md",
    "requiresLicenseKey": true,
    "requiresServerEndpoint": false,
    "platformIndex": "https://ai.syncfusion.com/react/llms.txt",
    "skillPack": "syncfusion/react-ui-components-skills",
    "docs": "https://ai.syncfusion.com/gallery/react/ts/diagram-mind-map.md"
  },
  "files": [
    {
      "target": "src/components/diagram-mind-map/Diagram.tsx",
      "content": "import {\n  // React Diagram components and utilities\n  MindMap as MindMapModule,\n  HierarchicalTree,\n  ConnectorConstraints,\n  DiagramConstraints,\n  ToolBase,\n  type MouseEventArgs,\n  randomId,\n  type ISelectionChangeEventArgs,\n  PointPort,\n  type UserHandleModel,\n  SelectorConstraints,\n  SnapConstraints,\n  type PointPortModel,\n  PortVisibility,\n  DiagramComponent,\n  NodeConstraints,\n  type NodeModel,\n  type ConnectorModel,\n  Node,\n  Connector,\n  Diagram,\n  DiagramTools,\n  Inject,\n  DataBinding,\n  type Side,\n  type MarginModel,\n  type HorizontalAlignment,\n  type VerticalAlignment,\n  type TextModel\n} from \"@syncfusion/ej2-react-diagrams\";\n\n// Importing base components and data management utilities\nimport { DataManager, Query } from \"@syncfusion/ej2-data\";\n\nlet mindMap = [\n    { id: 1, Label: 'Creativity', fill: 'red', branch: 'Root' },\n    { id: 3, Label: 'Brainstorming', parentId: 1, branch: 'Right', fill: 'red' },\n    { id: 4, Label: 'Complementing', parentId: 1, branch: 'Left', fill: 'red' },\n    { id: 22, Label: 'Sessions', parentId: 3, branch: 'subRight', fill: 'red' },\n    { id: 23, Label: 'Generate', parentId: 3, branch: 'subRight', fill: 'red' },\n    { id: 25, Label: 'Local', parentId: 22, branch: 'subRight' },\n    { id: 26, Label: 'Remote', parentId: 22, branch: 'subRight' },\n    { id: 27, Label: 'Individual', parentId: 22, branch: 'subRight' },\n    { id: 28, Label: 'Teams', parentId: 22, branch: 'subRight' },\n    { id: 29, Label: 'Ideas', parentId: 23, branch: 'subRight' },\n    { id: 30, Label: 'Engagement', parentId: 23, branch: 'subRight' },\n    { id: 31, Label: 'Product', parentId: 29, branch: 'subRight' },\n    { id: 32, Label: 'Service', parentId: 29, branch: 'subRight' },\n    { id: 33, Label: 'Business Direction', parentId: 29, branch: 'subRight' },\n    { id: 34, Label: 'Empowering', parentId: 30, branch: 'subRight' },\n    { id: 35, Label: 'Ownership', parentId: 30, branch: 'subRight' },\n    { id: 50, Label: 'Information', parentId: 4, branch: 'subLeft' },\n    { id: 51, Label: 'Expectations', parentId: 4, branch: 'subLeft' },\n    { id: 53, Label: 'Competitors', parentId: 50, branch: 'subLeft' },\n    { id: 54, Label: 'Products', parentId: 50, branch: 'subLeft' },\n    { id: 55, Label: 'Features', parentId: 50, branch: 'subLeft' },\n    { id: 56, Label: 'Other Data', parentId: 50, branch: 'subLeft' },\n    { id: 59, Label: 'Organization', parentId: 51, branch: 'subLeft' },\n    { id: 60, Label: 'Customer', parentId: 51, branch: 'subLeft' },\n    { id: 61, Label: 'Staff', parentId: 51, branch: 'subLeft' },\n    { id: 62, Label: 'Stakeholders', parentId: 51, branch: 'subLeft' },\n];\n\n// Initialize data manager with mindMap data and query\nlet items: DataManager = new DataManager(\n  mindMap as JSON[],\n  new Query().take(7)\n);\n\nlet diagramInstance: Diagram;\n// React component for MindMap\nfunction MindMap() {\n  function rendereComplete() {\n    diagramInstance.fitToPage();\n  }\n  //creation of the Ports\n  function getPort(): PointPortModel[] {\n    let port: PointPortModel[] = [\n      {\n        id: \"port1\",\n        offset: { x: 0, y: 0.5 },\n        visibility: PortVisibility.Hidden,\n        style: { fill: \"black\" }\n      },\n      {\n        id: \"port2\",\n        offset: { x: 1, y: 0.5 },\n        visibility: PortVisibility.Hidden,\n        style: { fill: \"black\" }\n      }\n    ];\n    return port;\n  }\n\n  // Function to add a new node\n  function addNode(): NodeModel {\n    let obj: NodeModel = {};\n    obj.id = randomId();\n    obj.data = {};\n    (obj.data as EmployeeInfo).Label = \"Node\";\n    return obj;\n  }\n\n  // Function to add a connector\n  function addConnector(source: NodeModel, target: NodeModel): ConnectorModel {\n    let connector: ConnectorModel = {};\n    connector.id = randomId();\n    connector.sourceID = source.id;\n    connector.targetID = target.id;\n    return connector;\n  }\n  // Function to get custom tool based on action (leftHandle, rightHandle, delete)\n  function getTool(action: string): ToolBase {\n    let tool: ToolBase;\n    if (action === \"leftHandle\") {\n      tool = new LeftExtendTool(diagramInstance.commandHandler);\n    } else if (action === \"rightHandle\") {\n      tool = new RightExtendTool(diagramInstance.commandHandler);\n    } else if (action === \"delete\") {\n      tool = new DeleteClick(diagramInstance.commandHandler);\n    }\n    return tool;\n  }\n\n  // Custom tool class for left extension tool\n  class LeftExtendTool extends ToolBase {\n    public mouseDown(args: MouseEventArgs): void {\n      super.mouseDown(args);\n      this.inAction = true;\n    }\n    public mouseUp(args: MouseEventArgs): void {\n      if (this.inAction) {\n        let selectedObject: any = this.commandHandler.getSelectedObject();\n        if (selectedObject[0]) {\n          if (selectedObject[0] instanceof Node) {\n            let node: NodeModel = addNode();\n            if ((selectedObject[0].data as EmployeeInfo).branch === \"Root\") {\n              (node.data as EmployeeInfo).branch = \"Right\";\n            } else if (\n              (selectedObject[0].data as EmployeeInfo).branch === \"Right\" ||\n              (selectedObject[0].data as EmployeeInfo).branch === \"subRight\"\n            ) {\n              (node.data as EmployeeInfo).branch = \"subRight\";\n            }\n            getTextEditValue(selectedObject[0], node);\n          }\n        }\n      }\n    }\n  }\n\n  // Custom tool class for right extension tool\n  class RightExtendTool extends ToolBase {\n    //mouseDown event\n    public mouseDown(args: MouseEventArgs): void {\n      super.mouseDown(args);\n      this.inAction = true;\n    }\n    //mouseDown event\n    public mouseUp(args: MouseEventArgs): void {\n      if (this.inAction) {\n        let selectedObject: any = this.commandHandler.getSelectedObject();\n        if (selectedObject[0]) {\n          if (selectedObject[0] instanceof Node) {\n            let node: NodeModel = addNode();\n            if ((selectedObject[0].data as EmployeeInfo).branch === \"Root\") {\n              (node.data as EmployeeInfo).branch = \"Left\";\n            } else if (\n              (selectedObject[0].data as EmployeeInfo).branch === \"Left\" ||\n              (selectedObject[0].data as EmployeeInfo).branch === \"subLeft\"\n            ) {\n              (node.data as EmployeeInfo).branch = \"subLeft\";\n            }\n            getTextEditValue(selectedObject[0], node);\n          }\n        }\n      }\n    }\n  }\n\n  // Custom tool class for delete tool\n  class DeleteClick extends ToolBase {\n    //mouseDown event\n    public mouseDown(args: MouseEventArgs): void {\n      super.mouseDown(args);\n      this.inAction = true;\n    }\n    //mouseup event\n    public mouseUp(args: MouseEventArgs): void {\n      if (this.inAction) {\n        let selectedObject: any = this.commandHandler.getSelectedObject();\n        if (selectedObject[0]) {\n          if (selectedObject[0] instanceof Node) {\n            let node: Node = selectedObject[0] as Node;\n            this.removeSubChild(node);\n          }\n          diagramInstance.doLayout();\n        }\n      }\n    }\n    //Function to Remove the subchild Elements\n    private removeSubChild(node: Node): void {\n      for (let i: number = node.outEdges.length - 1; i >= 0; i--) {\n        let connector: Connector = diagramInstance.getObject(\n          node.outEdges[i]\n        ) as Connector;\n        let childNode: Node = diagramInstance.getObject(\n          connector.targetID\n        ) as Node;\n        if (childNode.outEdges.length > 0) {\n          this.removeSubChild(childNode);\n        } else {\n          diagramInstance.remove(childNode);\n        }\n      }\n      diagramInstance.remove(node);\n    }\n  }\n  //Function to hide the require userhandle.\n  function hideUserHandle(name: string): void {\n    for (let handle of diagramInstance.selectedItems.userHandles) {\n      if (handle.name === name) {\n        handle.visible = false;\n      }\n    }\n  }\n  // Definition of left arrow path,right arrow path,delete icon path for user handle\n  let leftarrow: string =\n    \"M11.924,6.202 L4.633,6.202 L4.633,9.266 L0,4.633 L4.632,0 L4.632,3.551 L11.923,3.551 L11.923,6.202Z\";\n  let rightarrow: string =\n    \"M0,3.063 L7.292,3.063 L7.292,0 L11.924,4.633 L7.292,9.266 L7.292,5.714 L0.001,5.714 L0.001,3.063Z\";\n  let deleteicon: string =\n    \"M 7.04 22.13 L 92.95 22.13 L 92.95 88.8 C 92.95 91.92 91.55 94.58 88.76\" +\n    \"96.74 C 85.97 98.91 82.55 100 78.52 100 L 21.48 100 C 17.45 100 14.03 98.91 11.24 96.74 C 8.45 94.58 7.04\" +\n    \"91.92 7.04 88.8 z M 32.22 0 L 67.78 0 L 75.17 5.47 L 100 5.47 L 100 16.67 L 0 16.67 L 0 5.47 L 24.83 5.47 z\";\n\n  // Definition of user handles with their respective properties\n  let leftuserhandle: UserHandleModel = setUserHandle(\n    //it is in dedicated line here.\n    \"leftHandle\",\n    leftarrow,\n    \"Left\",\n    1,\n    { top: 0, bottom: 0, left: 0, right: 10 },\n    \"Left\",\n    \"Top\"\n  );\n  let rightuserhandle: UserHandleModel = setUserHandle(\n    //it is in dedicated line here.\n    \"rightHandle\",\n    rightarrow,\n    \"Right\",\n    1,\n    { top: 0, bottom: 0, left: 10, right: 0 },\n    \"Right\",\n    \"Top\"\n  );\n  let deleteuserhandle: UserHandleModel = setUserHandle(\n    //it is in dedicated line here.\n    \"delete\",\n    deleteicon,\n    \"Top\",\n    0.5,\n    { top: 0, bottom: 10, left: 0, right: 0 },\n    \"Center\",\n    \"Center\"\n  );\n  let handle: UserHandleModel[] = [\n    leftuserhandle,\n    rightuserhandle,\n    deleteuserhandle\n  ];\n  //set and creation of the Userhandle.\n  function setUserHandle( //it is in dedicated line here.\n    name: string,\n    pathData: string,\n    side: Side,\n    offset: number,\n    margin: MarginModel,\n    HorizontalAlignment: HorizontalAlignment,\n    verticalAlignment: VerticalAlignment\n  ): UserHandleModel {\n    let userhandle: UserHandleModel = {\n      name: name,\n      pathData: pathData,\n      backgroundColor: \"black\",\n      pathColor: \"white\",\n      side: side,\n      offset: offset,\n      margin: margin,\n      horizontalAlignment: HorizontalAlignment,\n      verticalAlignment: verticalAlignment\n    };\n    return userhandle;\n  }\n\n  // Function to handle text edit value and add a new node and connector\n  function getTextEditValue(selectObject:any, node:any){\n    let connector:any = addConnector(selectObject, node);\n    diagramInstance.clearSelection();\n   var newNode = diagramInstance.add(node);\n   diagramInstance.add(connector);\n   diagramInstance.doLayout();\n   diagramInstance.bringIntoView(newNode.wrapper.bounds);\n   diagramInstance.select([diagramInstance.nameTable[newNode.id]]);\n   diagramInstance.startTextEdit(diagramInstance.selectedItems.nodes[0]);\n  }\n  //Change the Position of the UserHandle.\n  function changeUserHandlePosition(change: string): void {\n    for (let handle of diagramInstance.selectedItems.userHandles) {\n      if (handle.name === \"delete\" && change === \"leftHandle\") {\n        applyHandle(\n          handle,\n          \"Left\",\n          1,\n          { top: 0, bottom: 0, left: 0, right: 10 },\n          \"Left\",\n          \"Top\"\n        );\n      } else if (handle.name === \"delete\" && change === \"rightHandle\") {\n        applyHandle(\n          handle,\n          \"Right\",\n          1,\n          { top: 0, bottom: 0, left: 10, right: 0 },\n          \"Right\",\n          \"Top\"\n        );\n      }\n    }\n  }\n  //function to set the value for UserHandle element.\n  function applyHandle( //it is in dedicated line here.\n    handle: UserHandleModel,\n    side: Side,\n    offset: number,\n    margin: MarginModel,\n    HorizontalAlignment: HorizontalAlignment,\n    verticalAlignment: VerticalAlignment\n  ): void {\n    handle.side = side;\n    handle.offset = offset;\n    handle.margin = margin;\n    handle.horizontalAlignment = HorizontalAlignment;\n    handle.verticalAlignment = verticalAlignment;\n  }\n  return (\n    <div className=\"control-pane\">\n      <div className=\"control-section\">\n        <div  style={{ width: \"100%\" }}>\n          <DiagramComponent\n            ref={diagram => (diagramInstance = diagram)}\n            id=\"diagram\"\n            style={{ width: \"74%\", height: \"550px\", float: \"left\" }}\n            width={\"100%\"}\n            height={\"550px\"}\n            constraints={\n              DiagramConstraints.Default & ~DiagramConstraints.UndoRedo\n            }\n            snapSettings={{ constraints: SnapConstraints.None }}\n            tool={DiagramTools.SingleSelect}\n            layout={{\n              type: \"MindMap\",\n              orientation:'Horizontal',\n              getBranch: (node: NodeModel, nodes: NodeModel[]) => {\n                return ((node as Node).data as EmployeeInfo).branch;\n              },\n              horizontalSpacing: 50\n            }}\n            //Selectionchange event for Node and connector\n            selectionChange={(arg: ISelectionChangeEventArgs) => {\n              if (arg.state === \"Changing\") {\n                if (arg.newValue[0] instanceof Node) {\n                  for (let handle of diagramInstance.selectedItems\n                    .userHandles) {\n                    handle.visible = true;\n                  }\n                  if (\n                    ((arg.newValue[0] as Node).data as EmployeeInfo)\n                      .branch === \"Left\" ||\n                    ((arg.newValue[0] as Node).data as EmployeeInfo)\n                      .branch === \"subLeft\"\n                  ) {\n                    hideUserHandle(\"leftHandle\");\n                    changeUserHandlePosition(\"leftHandle\");\n                  } else if (\n                    ((arg.newValue[0] as Node).data as EmployeeInfo)\n                      .branch === \"Right\" ||\n                    ((arg.newValue[0] as Node).data as EmployeeInfo)\n                      .branch === \"subRight\"\n                  ) {\n                    hideUserHandle(\"rightHandle\");\n                    changeUserHandlePosition(\"rightHandle\");\n                  } else if (\n                    ((arg.newValue[0] as Node).data as EmployeeInfo)\n                      .branch === \"Root\"\n                  ) {\n                    hideUserHandle(\"delete\");\n                  }\n                } else {\n                  hideUserHandle(\"leftHandle\");\n                  hideUserHandle(\"rightHandle\");\n                  hideUserHandle(\"delete\");\n                }\n              }\n            }}\n            selectedItems={{\n              constraints: SelectorConstraints.UserHandle,\n              userHandles: handle\n            }}\n            dataSourceSettings={{\n              id: \"id\",\n              parentId: \"parentId\",\n              dataSource: items,\n              root: String(1)\n            }}\n            //sets node default value\n            getNodeDefaults={(obj: Node) => {\n              obj.constraints =\n                NodeConstraints.Default & ~NodeConstraints.Drag;\n              if (\n                (obj.data as EmployeeInfo).branch === \"Left\" ||\n                (obj.data as EmployeeInfo).branch === \"Right\" ||\n                (obj.data as EmployeeInfo).branch === \"Root\"\n              ) {\n                obj.shape = { type: \"Basic\", shape: \"Ellipse\" };\n                obj.borderColor =\n                  \"black\"; /* tslint:disable:no-string-literal */\n                obj.style = {\n                  fill:\n                    (obj.data as EmployeeInfo).branch === \"Root\"\n                      ? \"#E74C3C\"\n                      : \"#F39C12\",\n                  strokeColor: \"none\",\n                  strokeWidth: 2\n                };\n                obj.annotations = [\n                  {\n                    content: (obj.data as EmployeeInfo).Label,\n                    margin: { left: 10, right: 10, top: 10, bottom: 10 },\n                    style: { color: \"white\" }\n                  }\n                ];\n                let port: PointPortModel[] = getPort();\n                for (let i: number = 0; i < port.length; i++) {\n                  obj.ports.push(new PointPort(obj, \"ports\", port[i], true));\n                }\n              } else {\n                let color: string; /* tslint:disable:no-string-literal */\n                if (\n                  (obj.data as EmployeeInfo).branch === \"Right\" ||\n                  (obj.data as EmployeeInfo).branch === \"subRight\"\n                ) {\n                  color = \"#8E44AD\";\n                } else {\n                  color = \"#3498DB\";\n                }\n                obj.shape = { type: \"Basic\", shape: \"Rectangle\" };\n                obj.style = { fill: color, strokeWidth: 0 };\n                obj.minWidth = 100;\n                obj.height = 4;\n                let port: PointPortModel[] = getPort();\n                for (let i: number = 0; i < port.length; i++) {\n                  obj.ports.push(new PointPort(obj, \"ports\", port[i], true));\n                }\n                obj.annotations = [\n                  {\n                    content: (obj.data as EmployeeInfo).Label,\n                    offset: { x: 0.5, y: 0 },\n                    verticalAlignment: \"Bottom\"\n                  }\n                ];\n                (obj.shape as TextModel).margin = {\n                  left: 0,\n                  right: 0,\n                  top: 0,\n                  bottom: 0\n                };\n              }\n              return obj;\n            }}\n            //sets connector default value\n            getConnectorDefaults={(\n              connector: ConnectorModel,\n              diagram: Diagram\n            ) => {\n              connector.type = \"Bezier\";\n              connector.targetDecorator = { shape: \"None\" };\n              let sourceNode: Node = diagram.getObject(\n                connector.sourceID\n              ) as Node;\n              let targetNode: Node = diagram.getObject(\n                connector.targetID\n              ) as Node;\n              if (\n                (targetNode.data as EmployeeInfo).branch === \"Right\" ||\n                (targetNode.data as EmployeeInfo).branch === \"subRight\"\n              ) {\n                connector.sourcePortID = sourceNode.ports[0].id;\n                connector.targetPortID = targetNode.ports[1].id;\n                connector.style = { strokeWidth: 5, strokeColor: \"#8E44AD\" };\n              } else if (\n                (targetNode.data as EmployeeInfo).branch === \"Left\" ||\n                (targetNode.data as EmployeeInfo).branch === \"subLeft\"\n              ) {\n                connector.sourcePortID = sourceNode.ports[1].id;\n                connector.targetPortID = targetNode.ports[0].id;\n                connector.style = { strokeWidth: 5, strokeColor: \"#3498DB\" };\n              }\n              connector.constraints &= ~ConnectorConstraints.Select;\n              return connector;\n            }}\n            created={() => {\n              diagramInstance.fitToPage();\n            }}\n            getCustomTool={getTool}\n            scrollSettings={{\n              //Sets the scroll padding\n              padding: { left: 50, right: 50 }\n            }}\n          >\n            <Inject\n              services={[DataBinding, MindMapModule, HierarchicalTree]}\n            />\n          </DiagramComponent>\n        </div>\n      </div>\n    </div>\n  );\n}\n\n// Interface for defining employee information\nexport interface EmployeeInfo {\n  branch: string;\n  color: string;\n  Left: string;\n  Right: string;\n  Root: string;\n  Label: string;\n}\nexport default MindMap;"
    }
  ]
}