Yes I have seen the mentioned example, but I did not manage to apply it correctly in my component.
Here is the full content of my component as an example:
import { Box, Button } from '@mui/material';
import React, { useEffect, useState, useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import * as go from 'gojs';
import key from '../common/gojs';
import { ReactDiagram } from 'gojs-react';
import { setSelectedElement } from '../../redux/EditionSlice';
import type { RootState } from '../../redux/store';
import CollaborationsController from '../../api/collaborations';
import { ArtifactElement } from '../../data/artifacts';
import GenericNode from './GenericNode';
const LinksPanel = (props: any) => {
const collaborations = CollaborationsController.getInstance();
const dispatch = useDispatch();
const diagRef = useRef<ReactDiagram>(null);
const activeCollabHeader = useSelector((state: RootState) => state.collab.activeCollaboration);
const [nodes, setNodes] = useState<go.ObjectData[]>([]);
const [links, setLinks] = useState<go.ObjectData[]>([]);
const [isNew, setNew] = useState(true);
const nodeClicked = (e: any, obj: any) => {
dispatch(setSelectedElement(obj.data.element as ArtifactElement));
};
const initDiagram = () => {
const $ = go.GraphObject.make;
go.Diagram.licenseKey = key;
const diagram = $(go.Diagram, {
"undoManager.isEnabled": true,
allowDragOut: true,
allowDrop: true,
allowTextEdit: true,
});
// Define node templates
diagram.nodeTemplate = GenericNode(nodeClicked);
// Define link template
diagram.linkTemplate = $(go.Link, {
layerName: "Foreground",
routing: go.Link.AvoidsNodes,
corner: 10,
toShortLength: 4,
relinkableFrom: true,
relinkableTo: true,
reshapable: true,
resegmentable: true
}, $(go.Shape, { strokeWidth: 1.5 }), $(go.Shape, { toArrow: "standard", stroke: null }), $(go.Panel, "Auto", new go.Binding("location", "location", go.Point.parse), $(go.Shape, {
fill: $(go.Brush, "Radial", { 0: "rgb(245, 245, 245)", 0.7: "rgb(245, 245, 245)", 1: "rgba(245, 245, 245, 0)" }),
stroke: null
}), $(go.TextBlock, {
textAlign: "center",
font: "9pt helvetica, arial, sans-serif",
margin: 4,
editable: true,
segmentOffset: new go.Point(0, -10)
}, new go.Binding("text", "label").makeTwoWay())));
// Set model
diagram.model = new go.GraphLinksModel({
linkKeyProperty: 'id'
});
diagram.addDiagramListener("PartCreated", function(e) {
const part = e.subject;
if (part instanceof go.Link) {
part.data.label = "links";
}
}); //FIXME: does not work
return diagram;
};
const handleSave = () => {
if (activeCollabHeader == null) return;
const diagram = diagRef.current?.getDiagram();
const model = diagram?.model as go.GraphLinksModel;
if (model == undefined) return;
const data = {
nodes: model.nodeDataArray,
links: model.linkDataArray
};
// TODO: send data to server
};
useEffect(() => {
const handleDndEvent = (event: any) => {
event.preventDefault();
};
const handleDropEvent = (event: any) => {
event.preventDefault();
const data: { [key: string]: any } = JSON.parse(event.dataTransfer.getData("text"));
const diagram = diagRef.current?.getDiagram();
const model = diagram?.model as go.GraphLinksModel;
if (model == undefined) return;
const dragged = event.target;
const pixelratio = diagram?.computePixelRatio();
const bbox = dragged.getBoundingClientRect();
let bbw = bbox.width;
if (bbw === 0) bbw = 0.001;
let bbh = bbox.height;
if (bbh === 0) bbh = 0.001;
const mx = event.clientX - bbox.left * ((dragged.width / pixelratio!!) / bbw);
const my = event.clientY - bbox.top * ((dragged.height / pixelratio!!) / bbh);
const point = diagram?.transformViewToDoc(new go.Point(mx, my));
// Convert the client coordinates to diagram coordinates
const docPoint = diagram!!.transformViewToDoc(new go.Point(mx - dragged.offsetX, my - dragged.offsetY));
diagram?.startTransaction('new node');
model.addNodeData({
key: data['id'],
type: data['type'].split('#')[1],
name: data['http://xowl.org/schema#name'],
category: data['type'].split('#')[1],
location: `${docPoint?.x} ${docPoint?.y}`,
element: data
});
diagram?.commitTransaction('new node');
};
const diagClassName = diagRef.current?.props.divClassName;
if (diagClassName != null) {
const diagElems = document.getElementsByClassName(diagClassName);
if (diagElems.length > 0) {
const diagElem = diagElems[0];
diagElem.addEventListener("drag", handleDndEvent);
diagElem.addEventListener("dragenter", handleDndEvent);
diagElem.addEventListener("dragleave", handleDndEvent);
diagElem.addEventListener("dragend", handleDndEvent);
diagElem.addEventListener("dragover", handleDndEvent);
diagElem.addEventListener("drop", handleDropEvent);
}
};
return () => {
const diagClassName = diagRef.current?.props.divClassName;
if (diagClassName != null) {
const diagElems = document.getElementsByClassName(diagClassName);
if (diagElems.length > 0) {
const diagElem = diagElems[0];
diagElem.removeEventListener("drag", handleDndEvent);
diagElem.removeEventListener("dragenter", handleDndEvent);
diagElem.removeEventListener("dragleave", handleDndEvent);
diagElem.removeEventListener("dragend", handleDndEvent);
diagElem.removeEventListener("dragover", handleDndEvent);
diagElem.removeEventListener("drop", handleDropEvent);
}
};
}
}, [diagRef]);
return (
<Box>
<ReactDiagram
ref={diagRef}
initDiagram={initDiagram}
divClassName='diagram-editor'
nodeDataArray={nodes}
linkDataArray={links}
/>
<Box sx={{ textAlign: 'center' }}>
<Button variant="contained" sx={{ borderRadius: 5 }} onClick={handleSave}>
Save
</Button>
</Box>
</Box>
);
};
export default LinksPanel;
For specific reasons, I could not use the palette provided by gojs.
Thank you for your help.