{
  "$schema": "https://ai.syncfusion.com/schema/registry-item.json",
  "name": "diagram-mind-map",
  "framework": "angular",
  "type": "registry:component",
  "component": "Diagram",
  "variant": "mind-map",
  "dependencies": [
    "@syncfusion/ej2-angular-diagrams"
  ],
  "description": "Horizontal mind-map diagram with custom user handles, draggable branches, and add/delete tools.",
  "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/angular/llms.txt",
    "skillPack": "syncfusion/angular-ui-components-skills",
    "docs": "https://ai.syncfusion.com/gallery/angular/diagram-mind-map.md"
  },
  "files": [
    {
      "target": "src/components/diagram-mind-map/mind-map.component.ts",
      "content": "import { Component, ViewEncapsulation, ViewChild } from '@angular/core';\nimport { DiagramComponent, Diagram, ConnectorModel, Connector, Node, HierarchicalTree, DataBinding, PointPortModel, randomId, SnapSettingsModel, PortVisibility, MindMap, UserHandleModel, SelectorConstraints, ToolBase, MouseEventArgs,DiagramConstraints, SnapConstraints, NodeModel, ISelectionChangeEventArgs, DiagramTools, NodeConstraints, SelectorModel, MarginModel, VerticalAlignment, HorizontalAlignment, Side, TextModel, ConnectorConstraints, PointPort, DiagramModule, ScrollSettingsModel, UndoRedo } from '@syncfusion/ej2-angular-diagrams';\n\nimport { DataManager } from '@syncfusion/ej2-data';\nimport { mindMap } from '../diagram-data';\n// Inject required diagram features\nDiagram.Inject(DataBinding, MindMap, HierarchicalTree);\n\n/**\n *  Sample component for Mind Map Tree using Syncfusion Diagram.\n */\n@Component({\n    selector: 'control-content',\n    templateUrl: 'mind-map.html',\n    encapsulation: ViewEncapsulation.None,\n    standalone: true,\n    imports: [ DiagramModule]\n})\nexport class MindMapDiagramComponent {\n  @ViewChild('diagram') public diagram!: DiagramComponent;\n  // Diagram tool settings\n  public tool: DiagramTools = DiagramTools.SingleSelect |\n    DiagramTools.MultipleSelect;\n  public scrollSettings?: ScrollSettingsModel;\n  public constraints!: DiagramConstraints;\n  ngOnInit(): void {\n    this.constraints= DiagramConstraints.Default &~(DiagramConstraints.UndoRedo)\n      // Defines the pageSettings for the diagram\n      this.scrollSettings = {\n          //Sets the scroll limit\n          padding: { right: 50, left: 50 }\n      }\n  }\n  // Data binding setup\n  public items: DataManager = new DataManager(mindMap);\n  public data: Object = { id: 'id', parentId: 'parentId', dataSource: this.items, root: '1' };\n  // Layout configuration\n  public layout: Object = {\n    type: 'MindMap',orientation:'Horizontal', horizontalSpacing: 50,\n    getBranch: (node: NodeModel, nodes: NodeModel[]) => {\n      return ((node as Node).data as EmployeeInfo).branch;\n    }\n  };\n  // Snap settings\n  public snapSettings: SnapSettingsModel = { constraints: SnapConstraints.None };\n  // Lifecycle hook to create the diagram\n  public create(args: Object): void {\n    this.diagram.fitToPage();\n  }\n\n  public getNodeDefaults: Function = this.nodeDefaults.bind(this);\n  public getConnDefaults: Function = this.getConnectorDefaults.bind(this);\n  public getCustomTool: Function = this.getTool.bind(this);\n   // Tool function to retrieve specific tool based on action\n  public getTool(action: string): ToolBase {\n    let tool: ToolBase = null!;\n    if (action === 'leftHandle') {\n      let leftTool: LeftExtendTool = new LeftExtendTool(this.diagram.commandHandler);\n      leftTool.diagram = this.diagram;\n      return leftTool;\n    } else if (action === 'rightHandle') {\n      let rightTool: RightExtendTool = new RightExtendTool(this.diagram.commandHandler);\n      rightTool.diagram = this.diagram;\n      return rightTool;\n    } else if (action === 'delete') {\n      let deleteTool: DeleteClick = new DeleteClick(this.diagram.commandHandler);\n      deleteTool.diagram = this.diagram;\n      return deleteTool;\n    }\n    return tool!;\n  }\n  // Function to define default node properties\n  private nodeDefaults(obj: NodeModel): NodeModel {\n    obj.constraints = NodeConstraints.Default & ~NodeConstraints.Drag;\n    let empInfo: EmployeeInfo = obj.data as EmployeeInfo;\n    if (empInfo.branch === 'Root' || empInfo.branch === 'Left' || empInfo.branch === 'Right') {\n      obj.shape = { type: 'Basic', shape: 'Ellipse' };\n      obj.borderColor = 'black';\n      obj.style = { fill: empInfo.branch === 'Root' ? '#E74C3C' : '#F39C12', strokeColor: 'none', strokeWidth: 2 };\n      obj.annotations = [\n        { content: empInfo.Label, margin: { left: 10, right: 10, top: 10, bottom: 10 }, style: { color: 'white' } }\n      ];\n    } else {\n      let color: string = empInfo.branch === 'subRight' ? '#8E44AD' : '#3498DB';\n      obj.shape = { type: 'Basic', shape: 'Rectangle' };\n      obj.style = { fill: color, strokeWidth: 0 };\n      obj.minWidth = 100;\n      obj.height = 4;\n      obj.annotations = [{ content: empInfo.Label, offset: { x: 0.5, y: 0 }, verticalAlignment: 'Bottom' }];\n      (obj.shape as TextModel).margin = { left: 0, right: 0, top: 0, bottom: 0 };\n    }\n    // Initialize ports for the node\n    let port: PointPortModel[] = this.getPort();\n    for (let i: number = 0; i < port.length; i++) {\n      obj.ports!.push(new PointPort(obj, 'ports', port[i], true));\n    }\n    return obj;\n  }\n\n  //sets connector default value\n  private getConnectorDefaults(connector: ConnectorModel, diagram: Diagram): ConnectorModel {\n    let sourceNode: Node = diagram.getObject(connector.sourceID!) as Node;\n    let targetNode: Node = diagram.getObject(connector.targetID!) as Node;\n    let empInfo: EmployeeInfo = targetNode.data as EmployeeInfo;\n    connector.type = 'Bezier';\n    connector.targetDecorator = { shape: 'None' };\n    connector.constraints! &= ~ConnectorConstraints.Select;\n    if (empInfo.branch === 'Right' || empInfo.branch === 'subRight') {\n      connector.sourcePortID = sourceNode.ports[0].id;\n      connector.targetPortID = targetNode.ports[1].id;\n      connector.style = { strokeWidth: 5, strokeColor: '#8E44AD' };\n    } else if (empInfo.branch === 'Left' || empInfo.branch === 'subLeft') {\n      connector.sourcePortID = sourceNode.ports[1].id;\n      connector.targetPortID = targetNode.ports[0].id;\n      connector.style = { strokeWidth: 5, strokeColor: '#3498DB' };\n    }\n    return connector;\n  }\n  // Initializes user handle\n  public handle: UserHandleModel[] = [\n    {\n      name: 'leftHandle', visible: true, backgroundColor: 'black', offset: 1, side: 'Left', pathColor: 'white',\n      pathData: '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      margin: { top: 0, bottom: 0, left: 0, right: 10 }, horizontalAlignment: 'Left', verticalAlignment: 'Top'\n    },\n    {\n      name: 'delete', side: 'Top', horizontalAlignment: 'Center', verticalAlignment: 'Center',\n      pathData:\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      margin: { top: 0, bottom: 10, left: 0, right: 0 }, offset: 0.5, pathColor: 'white'\n    },\n    {\n      name: 'rightHandle', offset: 1, horizontalAlignment: 'Right', verticalAlignment: 'Top', pathColor: 'white',\n      pathData: '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      side: 'Right', backgroundColor: 'black', margin: { top: 0, bottom: 0, left: 10, right: 0 }\n    }\n  ];\n\n  public selectedItems: SelectorModel = { constraints: SelectorConstraints.UserHandle, userHandles: this.handle };\n  public selectionChange(arg: ISelectionChangeEventArgs): void {\n    if (arg.state === 'Changing') {\n      if (arg.newValue[0] instanceof Node) {\n        let empInfo: EmployeeInfo = ((arg.newValue[0] as Node).data as EmployeeInfo);\n        for (let handle of this.diagram.selectedItems.userHandles!) {\n          handle.visible = true;\n        }\n        if (empInfo.branch === 'Left' || empInfo.branch === 'subLeft') {\n          this.hideUserHandle('leftHandle');\n          this.changeUserHandlePosition('leftHandle');\n        } else if (empInfo.branch === 'Right' || empInfo.branch === 'subRight') {\n          this.hideUserHandle('rightHandle');\n          this.changeUserHandlePosition('rightHandle');\n        } else if (empInfo.branch === 'Root') {\n          this.hideUserHandle('delete');\n        }\n      } else {\n        this.hideUserHandle('leftHandle');\n        this.hideUserHandle('rightHandle');\n        this.hideUserHandle('delete');\n      }\n    }\n  }\n\n  //hide the require userhandle.\n  public hideUserHandle(name: string): void {\n    for (let handle of this.diagram.selectedItems.userHandles!) {\n      if (handle.name === name) {\n        handle.visible = false;\n      }\n    }\n  }\n\n  //Change the Position of the UserHandle.\n  private changeUserHandlePosition(change: string): void {\n    for (let handle of this.diagram.selectedItems.userHandles!) {\n      if (handle.name === 'delete' && change === 'leftHandle') {\n        this.applyHandle(handle, 'Left', 1, { top: 0, bottom: 0, left: 0, right: 10 }, 'Left', 'Top');\n      } else if (handle.name === 'delete' && change === 'rightHandle') {\n        this.applyHandle(handle, 'Right', 1, { top: 0, bottom: 0, left: 10, right: 0 }, 'Right', 'Top');\n      }\n    }\n  }\n  //set the value for UserHandle element.\n  private applyHandle(\n    handle: UserHandleModel, side: Side, offset: number, margin: MarginModel,\n    HorizontalAlignment: HorizontalAlignment, VerticalAlignment: VerticalAlignment): void {\n    handle.side = side;\n    handle.offset = offset;\n    handle.margin = margin;\n    handle.horizontalAlignment = HorizontalAlignment;\n    handle.verticalAlignment = VerticalAlignment;\n  }\n  //Function to create ports for nodes\n  private getPort(): PointPortModel[] {\n    let port: PointPortModel[] = [\n      { id: 'port1', offset: { x: 0, y: 0.5 }, visibility: PortVisibility.Hidden, style: { fill: 'black' } },\n      { id: 'port2', offset: { x: 1, y: 0.5 }, visibility: PortVisibility.Hidden, style: { fill: 'black' } }\n    ];\n    return port;\n  }\n}\n// Function to add a new node\nfunction 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// Function to add a new connector\nfunction 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// Class for handling left extension tool\nclass LeftExtendTool extends ToolBase {\n  public diagram!: Diagram;\n  //mouseDown event\n  public override mouseDown(args: MouseEventArgs): void {\n    super.mouseDown(args);\n    this.inAction = true;\n  }\n  //mouseUp event\n  public override 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          let empInfo: EmployeeInfo = (selectedObject[0].data as EmployeeInfo);\n          if (empInfo.branch === 'Root') {\n            (node.data as EmployeeInfo).branch = 'Right';\n          } else if (empInfo.branch === 'Right' || empInfo.branch === 'subRight') {\n            (node.data as EmployeeInfo).branch = 'subRight';\n          }\n          let connector: ConnectorModel = addConnector(selectedObject[0], node);\n          this.diagram.clearSelection();\n          let newNode: Node = this.diagram.add(node) as Node;\n          this.diagram.add(connector);\n          this.diagram.doLayout();\n          this.diagram.bringIntoView(newNode.wrapper.bounds);\n          this.diagram.startTextEdit(newNode);\n        }\n      }\n    }\n  }\n}\n// Class for handling right extension tool\nclass RightExtendTool extends ToolBase {\n  public diagram!: Diagram;\n  //mouseDown event\n  public override mouseDown(args: MouseEventArgs): void {\n    super.mouseDown(args);\n    this.inAction = true;\n  }\n  //mouseUp event\n  public override 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          let empInfo: EmployeeInfo = (selectedObject[0].data as EmployeeInfo);\n          if (empInfo.branch === 'Root') {\n            (node.data as EmployeeInfo).branch = 'Left';\n          } else if (empInfo.branch === 'Left' || empInfo.branch === 'subLeft') {\n            (node.data as EmployeeInfo).branch = 'subLeft';\n          }\n          let connector: ConnectorModel = addConnector(selectedObject[0], node);\n          this.diagram.clearSelection();\n          let newNode: Node = this.diagram.add(node) as Node;\n          this.diagram.add(connector);\n          this.diagram.doLayout();\n          this.diagram.bringIntoView(newNode.wrapper.bounds);\n          this.diagram.startTextEdit(newNode);\n        }\n      }\n    }\n  }\n}\n// Class for handling delete tool\nclass DeleteClick extends ToolBase {\n  public diagram!: Diagram;\n  //mouseDown event\n  public override mouseDown(args: MouseEventArgs): void {\n    super.mouseDown(args);\n    this.inAction = true;\n  }\n  //mouseup event\n  public override 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        this.diagram.doLayout();\n      }\n    }\n  }\n  //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 = this.diagram.getObject(node.outEdges[i]) as Connector;\n      let childNode: Node = this.diagram.getObject(connector.targetID) as Node;\n      if (childNode.outEdges.length > 0) {\n        this.removeSubChild(childNode);\n      } else {\n        this.diagram.remove(childNode);\n      }\n    }\n    this.diagram.remove(node);\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}\n"
    },
    {
      "target": "src/components/diagram-mind-map/mind-map.html",
      "content": "\n<div class=\"col-lg-8 control-section\" style=\"width: 100%;\">\n    <div class=\"content-wrapper\">\n        <ejs-diagram #diagram id=\"diagram\" width=\"100%\" height=\"700px\" [getNodeDefaults]=getNodeDefaults [snapSettings]=snapSettings\n            [selectedItems]=\"selectedItems\" [getCustomTool]='getCustomTool' (selectionChange)=\"selectionChange($event)\" (created)=\"create($event)\"\n            [tool]=tool [layout]='layout' [dataSourceSettings]='data' [getConnectorDefaults]='getConnDefaults' [scrollSettings]=\"scrollSettings\" [constraints]=\"constraints\">\n        </ejs-diagram>\n    </div>\n</div>\n"
    }
  ]
}