Создайте функциональную подсказку для JSPlumb в Angular

Ожидаемый результат

Как я создалсоздал этоэтокнопки, нажмитеck функция. Я получаю данные, как ожидалось.

Чтобы использовать плагин всплывающей подсказки JSPlumb Tippy, вы должны сначала выполнить следующие предварительные условия:

  1. JSPlumbбиблиотека: Thэто JavaSскрипт библиотека. that обеспечиваетвозможностьвозможности визуальноподключения элементы на мыб страницед. Вывы можете вниз загрузить эту электронную библиотеку из отсюдана официальномофициальном веб-сайте или внутрина нем, используя пакет strong>agemanager like npm.

npm установить jsplumb

2. Tippy.js библиотека: это легкий, фургон illa JavaScript tooltip библиотекаary tht allows вы должны создаватьте клиентов, чтобы слишкомдать советы дляр вам на мыб странице. Вывы можете вниз загрузить библиотеку от с на официальном веб-сайте или вобновить его, используяна паккаге manager likeke npm.

npm установить типпи.js

3. После того, как у вас вы имеете эти предварительные требования, сайты установленустановлен иd настройкаt up.

давайте создадим пример проекта: -

создайте компонент узла приложения, где мы можем разместить наш код дизайна jsplumb

узел компонента ng g

<div class="content" role="main">
  <input type="text" (input)="addNode($event)">
  <label>No of Rows</label>
  <button (click)="createConnections()">Auto Connect</button>
  <div class="container">
    <div class="panel-one">
      <app-node [nodes]="nodes"></app-node>
    </div>
    <div class="vl"></div>
    <div class="panel-two"></div>
  </div>

  <router-outlet></router-outlet>
</div>

вставьте приведенный выше код в app.component.html.

В вышеве коде мы разместилие поместили вас r app-node componentвнутриблока div,

из app.component.ts мы передаем данные узлов компоненту узла приложения

import { Component, OnInit } from '@angular/core';
import { jsPlumb } from 'jsplumb';
import tippy from 'tippy.js';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  jsPlumbInstance = jsPlumb.getInstance();

  title = ' Angular jsPlumb Integration';
  nodes = [];
  index = 0;
  totalNodes: number = 0;
  ngOnInit() {
    this.addNode(6)
  }

  addNode(n?) {
    this.nodes = [];
    this.index = 0;
    // let n = num.target.value
    this.totalNodes = n;
    for (let i = 0; i < n; i++) {
      this.nodes.push({
        id: 'Node' + (this.index + 1),
        name: (this.index + 1),
        type: ''
      });
      this.index += 1;
    }
  }

  // This function creates connections between nodes using the JSPlumb library.
  createConnections() {
    // Create a new JSPlumb instance with a state machine connector and left/right anchors.
    const instance = jsPlumb.getInstance({
      Connector: 'StateMachine',
      Anchor: ['Left', 'Right'],
    });

    // Create an array of node IDs from the 'nodes' array.
    let nodeIds = []
    for (let i = 0; i < this.nodes.length - 1; i++) {
      nodeIds.push(this.nodes[i]["id"])
    }

    // Loop through the node IDs and connect each node to its next neighbor.
    for (let i = 0; i < nodeIds.length; i++) {
      // Get the source and target IDs for the current connection.
      const sourceId = nodeIds[i];
      const targetId = nodeIds[(i + 1) % nodeIds.length];

      // Connect the source and target nodes using the JSPlumb instance.
      instance.connect({
        source: this.nodes[i]["id"],
        target: this.nodes[i + 1]["id"],
      });

      // Call the 'autoConnectNodes' function to automatically connect endpoints between nodes.
      this.autoConnectNodes(instance, sourceId, targetId);
    }
  }


  // This function automatically connects endpoints between nodes and creates a tooltip with buttons when a connection is made.
  autoConnectNodes(instance, sourceId: string, targetId: string): void {
    // Use the JSPlumb instance to connect the source and target nodes with a rectangle endpoint.
    instance.connect({
      source: sourceId,
      target: targetId,
      anchors: ['Right', 'Left'],
      endpoint: 'Rectangle',
      paintStyle: {
        stroke: 'rgb(153, 203, 58)',
        strokeWidth: 2,
      },
    });

    // Create a tooltip with buttons when the user clicks on the source node.
    tippy(sourceId, {
      // Use HTML content to create a tooltip with buttons.
      content: '<div class="tooltip-btns"><button (click)="createCircuit(${this.destinationEndPoint})">1</button><button>2</button><button>close</button></div>',
      // Append the tooltip to the parent element.
      appendTo: 'parent',
      // Allow the tooltip to contain HTML content.
      allowHTML: true,
      // Position the tooltip at the bottom of the source node.
      placement: 'bottom',
      // Trigger the tooltip when the user clicks on the source node.
      trigger: 'click',
      // Make the tooltip interactive so the user can click on the buttons.
      interactive: true,
      // Add an arrow to the tooltip.
      arrow: true,
    })
  }


}

вставьте приведенный выше код в app.component.ts

онмыe создаем начальные узлы как 6 d передача th на our компонент узла.

<ng-template #nodes></ng-template>
<div class="modal-container"></div>

вставьте вышеve co в node.component.html

import { Component, OnInit, AfterViewInit, ViewChild, ViewContainerRef, Input, OnChanges } from '@angular/core';
import { NodeService } from './node.service';
import { SimpleModalService } from 'ngx-simple-modal';
import { DialogComponent } from '../dialog.component';

@Component({
  selector: 'app-node',
  templateUrl: './node.component.html',
  styleUrls: ['./node.component.scss']
})
export class NodeComponent implements OnChanges, AfterViewInit {
  @Input() nodes;
  @ViewChild('nodes', { read: ViewContainerRef, static: true }) viewContainerRef: ViewContainerRef;

  constructor(public nodeService: NodeService, public simpleModalService: SimpleModalService) {
  }

  // This method is called after the view has been initialized.
  ngAfterViewInit() {
    // Bind a 'connection' event to the JSPlumb instance.
    this.nodeService.jsPlumbInstance.bind('connection', info => {
      // When a connection is made, display a modal dialog and subscribe to the result.
      this.simpleModalService.addModal(DialogComponent, {
        title: 'Dialog',
        questions: { name: '', type: '' }
      })
        .subscribe((result) => {
          // When the modal dialog is closed, find the target node and update its name and type.
          const targetNode = this.nodes.find(node => node.id === info.targetId);
          if (targetNode) {
            targetNode.name = result.name;
            targetNode.type = result.type;
            // If the target node is an 'end' node, delete the right endpoint to prevent further connections.
            if (targetNode.type === 'end') {
              this.nodeService.jsPlumbInstance.deleteEndpoint(info.targetId + 'right');
            }
          }
        });
    });
  }

  // This method is called whenever the input properties of the component change.
  ngOnChanges() {
    // Delete all existing endpoints.
    this.nodeService.jsPlumbInstance.deleteEveryEndpoint();
    // Set the root view container reference for the node service.
    this.nodeService.setRootViewContainerRef(this.viewContainerRef);
    // Clear all nodes from the node service.
    this.nodeService.clear();
    // If there are any nodes, add them to the node service as dynamic nodes.
    if (this.nodes.length > 0) {
      this.nodes.forEach(node => {
        this.nodeService.addDynamicNode(node);
      });
    }
  }

}

вставьте вышеве co в node.component.ts

Создайтете неде.серверныйфайл, файл, который я имею создатьте этув внутрииде нетде фолддер егоэльф

import {
    ComponentFactoryResolver,
    Injectable
} from '@angular/core';
import { DynamicNodeComponent } from './dynamic-node.component';
import { jsPlumb } from 'jsplumb';

@Injectable({
    providedIn: 'root',
})
export class NodeService {

    jsPlumbInstance = jsPlumb.getInstance();
    private rootViewContainer: any;

    constructor(private factoryResolver: ComponentFactoryResolver) {
    }

    public setRootViewContainerRef(viewContainerRef) {
        this.rootViewContainer = viewContainerRef;
    }

    public addDynamicNode(node: any) {
        const factory = this.factoryResolver.resolveComponentFactory(DynamicNodeComponent);
        const component = factory.create(this.rootViewContainer.parentInjector);
        (<any>component.instance).node = node;
        (<any>component.instance).jsPlumbInstance = this.jsPlumbInstance;

        this.rootViewContainer.insert(component.hostView);
    }

    public clear() {
        this.rootViewContainer.clear();
    }
}

Вставьтете еевышеве коде внутриидеи нетде.серверvice.ts файл

Создайтете 2 большере компонентовкомпонентов dynamic-node.component.ts, файл dialog.component.ts

import { Component, Input, AfterViewInit } from '@angular/core';
import { SimpleModalService } from 'ngx-simple-modal';
import { DialogComponent } from '../dialog.component';
import tippy, { animateFill, followCursor, inlinePositioning } from 'tippy.js';

export interface Node {
    id: string;
    name: string;
    type: string;
}

@Component({
    selector: 'app-dynamic-node',
    template: `
  <div (dblclick)="editNode(node)" class="node"  id="{{node.id}}" style="top: 0; left: 50%;">
    {{node.name}}
    <!-- <i (click)="removeNode(node)" class="material-icons close">clear</i> -->
  </div>`,
    styles: [`
  .node {
    width: 30px;
    height: 25px;
    padding: 5px;
    margin: 10px;
    border-radius: 50%;
    text-align: center;
    position: relative;
    box-shadow: 0 10px 40px 0 #b0c1d9;
    }
 .close {
    font-size: 10px;
    position: absolute;
    right: 9px;
    top: 0px;
    cursor: pointer;
    }
  `]
})
export class DynamicNodeComponent implements AfterViewInit {

    @Input() node: Node;
    @Input() jsPlumbInstance;

    sourceEndPoint: any;
    destinationEndPoint: any;
    exampleDropOptions = {
        tolerance: 'touch',
        hoverClass: 'dropHover',
        activeClass: 'dragActive'
    };
    source = {
        endpoint: ['Dot', { radius: 7 }],
        paintStyle: { fill: '#99cb3a' },
        isSource: true,
        scope: 'jsPlumb_DefaultScope',
        connectorStyle: { stroke: '#99cb3a', strokeWidth: 3 },
        connector: ['Bezier', { curviness: 63 }],
        maxConnections: 1,
        isTarget: false,
        connectorOverlays: [['Arrow', { location: 1 }]],
        dropOptions: this.exampleDropOptions
    };
    destination = {
        endpoint: ['Dot', { radius: 4 }],
        paintStyle: { fill: '#ffcb3a' },
        isSource: false,
        scope: 'jsPlumb_DefaultScope',
        connectorStyle: { stroke: '#ffcb3a', strokeWidth: 6 },
        connector: ['Bezier', { curviness: 23 }],
        maxConnections: 1,
        isTarget: true,
        dropOptions: this.exampleDropOptions
    };

    constructor(private simpleModalService: SimpleModalService) { }

    ngOnInit() {
    }
    ngAfterViewInit() {
        this.sourceEndPoint = this.jsPlumbInstance.addEndpoint(this.node.id,
            {
                anchor: 'Right',
                uuid: this.node.id + 'right', endpoint: 'Dot',
                isSource: true,
                isTarget: true,
            }, this.source);
        this.destinationEndPoint = this.jsPlumbInstance.addEndpoint(this.node.id,
            {
                anchor: 'Left', uuid: this.node.id + 'left',
                maxConnections: 2
            }, this.destination);

        let tippyInstance = tippy(this.destinationEndPoint.canvas, {
            content: `<div class="tooltip-btns"><button data-info=${this.node.id} id ="tooltip-btn-1">1</button><button>2</button><button>close</button></div>`,
            appendTo: 'parent',
            allowHTML: true,
            placement: 'bottom',
            // trigger: 'manual',
            // trigger: 'click',
            interactive: true,
            arrow: true,
            animation: 'fade',
            animateFill: true,
            followCursor: true,
            inlinePositioning: true,

            plugins: [animateFill, followCursor, inlinePositioning],
            popperOptions: {
                strategy: 'fixed',
                modifiers: [
                    {
                        name: 'flip',
                        options: {
                            fallbackPlacements: ['bottom', 'right'],
                        },
                    },
                    {
                        name: 'preventOverflow',
                        options: {
                            altAxis: true,
                            tether: false,
                        },
                    },
                ],
            },
            onMount: (instance) => {
                const button = instance.popper.querySelector('#tooltip-btn-1');
                button.addEventListener('click', () => {
                    let info = button.getAttribute("data-info")
                    // Do something when the button is clicked
                    console.log(this.node, ' - ', info)
                }, { once: true });
            },
        })

        console.log(tippyInstance["popper"]);

    }

    createCircuit(data) {
        console.log(data)
    }

    removeNode(node) {
        this.jsPlumbInstance.removeAllEndpoints(node.id);
        this.jsPlumbInstance.remove(node.id);
    }

    editNode(node) {
        this.simpleModalService.addModal(DialogComponent, {
            title: 'Dialog',
            questions: { name: node.name, type: node.type }
        })
            .subscribe((result) => {
                this.node.name = result.name;
                this.node.type = result.type;
                if (node.type === 'end') {
                    this.jsPlumbInstance.deleteEndpoint(node.id + 'right');
                } else {
                    this.jsPlumbInstance.addEndpoint(this.node.id,
                        { anchor: 'Right', uuid: this.node.id + 'right' }, this.source);
                }
            });
    }
}

Вставьте приведенный выше код в dynamic-node.component.ts.

import { Component } from '@angular/core';
import { SimpleModalComponent } from 'ngx-simple-modal';

export interface DialogModel {
    title: string;
    questions: { name: '', type: '' };
}

@Component({
    selector: 'app-dialog',
    template: `
    <div class="modal-content">
      <div class="modal-header">
        <h4>{{title}}</h4>
      </div>
      <div class="modal-body">
        <label>Name</label>
        <!-- <input type="text" class="form-control" [(ngModel)]="questions['name']" name="name" />
        <label>Type</label>
        <select [(ngModel)]="questions['type']">
        <option value="middle">Middle</option>
        <option value="end">End</option>
        </select> -->
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-outline-danger" (click)="close()">Cancel</button>
        <button type="button" class="btn btn-primary" (click)="apply()">Save</button>
      </div>
    </div>
  `
})
export class DialogComponent extends SimpleModalComponent<DialogModel, DialogModel['questions']> implements DialogModel {
    title: string;
    questions: { name: '', type: '' };
    constructor() {
        super();
    }

    apply() {
        this.result = this.questions;
        this.close();
    }
}

Вставьте приведенный выше код в dialog.component.ts.

Мy Папкадер Структура: –

Дляр болееотносительной функции действительности ссылки на этот "JSP lumb» и Tippy.js Documentation

Заключение

Созданиеинструментаtip использованиеng Tippy.js в Angleular Angel достаточноэто простопле иd выи ар и идти strong>ing to needd createate one скоро раньше или позже э в тыр ап р.

Even if at firstst it may sou nd kindd of conf использование,you ar e идти ing to платаl удобствоrtable работакороль остроумие h вмид всехлихих преимуществах .