<body onload="init()">
<div style="width:100%; white-space:nowrap;">
<span style="display: inline-block; vertical-align: top">
<div class="palettezone">
<div class="draggable" draggable="true">Water</div>
<div class="draggable" draggable="true">Coffee</div>
<div class="draggable" draggable="true">Tea</div>
</div>
</span>
<span style="display: inline-block; vertical-align: top">
<div class="dropzone" id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px;"></div>
</span>
</div>
<p><a href="https://gojs.net"/>gojs.net<a/></p>
</body>
.draggable {
font: bold 16px sans-serif;
width: 140px;
height: 20px;
text-align: center;
background: white;
cursor: move;
margin-top: 20px;
}
.palettezone {
width: 160px;
height: 400px;
background: lightblue;
padding: 10px;
padding-top: 1px;
float: left;
}
function init() {
// *********************************************************
// First, set up the infrastructure to do HTML drag-and-drop
// *********************************************************
var dragged = null; // A reference to the element currently being dragged
// highlight stationary nodes/links during an external drag-and-drop into a Diagram
function highlight(part) { // may be null
var oldskips = myDiagram.skipsUndoManager;
myDiagram.skipsUndoManager = true;
myDiagram.startTransaction("highlight");
if (part !== null) {
myDiagram.highlight(part);
} else {
myDiagram.clearHighlighteds();
}
myDiagram.commitTransaction("highlight");
myDiagram.skipsUndoManager = oldskips;
}
// This event should only fire on the drag targets.
// Instead of finding every drag target,
// we can add the event to the document and disregard
// all elements that are not of class "draggable"
document.addEventListener("dragstart", function(event) {
if (event.target.className !== "draggable") return;
// Some data must be set to allow drag
event.dataTransfer.setData("text", event.target.textContent);
// store a reference to the dragged element
dragged = event.target;
// Objects during drag will have a red border
event.target.style.border = "2px solid red";
}, false);
// This event resets styles after a drag has completed (successfully or not)
document.addEventListener("dragend", function(event) {
// reset the border of the dragged element
dragged.style.border = "";
highlight(null);
}, false);
// Next, events intended for the drop target - the Diagram div
var div = document.getElementById("myDiagramDiv");
div.addEventListener("dragenter", function(event) {
// Here you could also set effects on the Diagram,
// such as changing the background color to indicate an acceptable drop zone
// Requirement in some browsers, such as Internet Explorer
event.preventDefault();
}, false);
div.addEventListener("dragover", function(event) {
// We call preventDefault to allow a drop
// But on divs that already contain an element,
// we want to disallow dropping
if (this === myDiagram.div) {
var can = event.target;
var pixelratio = window.PIXELRATIO;
// if the target is not the canvas, we may have trouble, so just quit:
if (!(can instanceof HTMLCanvasElement)) return;
var bbox = can.getBoundingClientRect();
var bbw = bbox.width;
if (bbw === 0) bbw = 0.001;
var bbh = bbox.height;
if (bbh === 0) bbh = 0.001;
var mx = event.clientX - bbox.left * ((can.width / pixelratio) / bbw);
var my = event.clientY - bbox.top * ((can.height / pixelratio) / bbh);
var point = myDiagram.transformViewToDoc(new go.Point(mx, my));
var curpart = myDiagram.findPartAt(point, false);
if (curpart instanceof go.Node || curpart instanceof go.Link) {
highlight(curpart);
} else {
highlight(null);
}
}
if (event.target.className === "dropzone") {
// Disallow a drop by returning before a call to preventDefault:
return;
}
// Allow a drop on everything else
event.preventDefault();
}, false);
div.addEventListener("dragleave", function(event) {
// reset background of potential drop target
if (event.target.className == "dropzone") {
event.target.style.background = "";
}
highlight(null);
}, false);
// handle the user option for removing dragged items from the Palette
var remove = document.getElementById('remove');
div.addEventListener("drop", function(event) {
// prevent default action
// (open as link for some elements in some browsers)
event.preventDefault();
// Dragging onto a Diagram
if (this === myDiagram.div) {
var can = event.target;
var pixelratio = window.PIXELRATIO;
// if the target is not the canvas, we may have trouble, so just quit:
if (!(can instanceof HTMLCanvasElement)) return;
myDiagram.startTransaction('new node');
var overPart = myDiagram.highlighteds.first();
var group = undefined;
if (overPart && overPart.containingGroup !== null) {
group = overPart.containingGroup.data.key;
}
var newData = {
key: event.dataTransfer.getData('text'),
color: "lightyellow",
group: group
}
myDiagram.model.addNodeData(newData);
if (overPart instanceof go.Node) {
// could do something for drop on node here
} else if (overPart instanceof go.Link) {
if (overPart.containingGroup !== null) {
console.log(overPart.containingGroup.data.key);
newData.group = overPart.containingGroup.data.key;
}
dropOntoLink(newData, overPart);
}
myDiagram.commitTransaction('new node');
myDiagram.animationManager.stopAnimation();
}
// If we were using drag data, we could get it here, ie:
// var data = event.dataTransfer.getData('text');
}, false);
function dropOntoLink(newData, link) {
var diagram = link.diagram;
// add links between old from/to and new node
var oldFrom = link.fromNode.data.key;
var oldTo = link.toNode.data.key;
var newKey = diagram.model.getKeyForNodeData(newData);
diagram.model.addLinkData({
from: oldFrom,
to: newKey
});
diagram.model.addLinkData({
from: newKey,
to: oldTo
});
// remove this link
diagram.model.removeLinkData(link.data);
}
// *********************************************************
// Second, set up a GoJS Diagram
// *********************************************************
var $ = go.GraphObject.make; // for conciseness in defining templates
myDiagram = $(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
"undoManager.isEnabled": true,
layout: $(go.TreeLayout, { angle: 90 })
});
window.PIXELRATIO = myDiagram.computePixelRatio(); // constant needed to determine mouse coordinates on the canvas
myDiagram.groupTemplate =
$(go.Group, "Auto",
{ layout: $(go.TreeLayout) },
$(go.Shape,
{ fill: "whitesmoke" }
),
$(go.Panel, "Vertical",
$(go.Panel, "Horizontal",
$("SubGraphExpanderButton", { margin: 3 }),
$(go.TextBlock, "Group",
{ margin: 3, font: "bold 16px sans-serif" }
)
),
$(go.Placeholder, { padding: 5 })
)
);
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto",
// { locationSpot: go.Spot.Center },
// new go.Binding("location"),
$(go.Shape, "Rectangle",
{ fill: 'white' },
// Shape.fill is bound to Node.data.color
new go.Binding("fill", "color"),
// this binding changes the Shape.fill when Node.isHighlighted changes value
new go.Binding("fill", "isHighlighted", function(h, shape) {
if (h) return "red";
var c = shape.part.data.color;
return c ? c : "white";
}).ofObject()), // binding source is Node.isHighlighted
$(go.TextBlock,
{ margin: 3, font: "bold 16px sans-serif", width: 140, textAlign: 'center' },
// TextBlock.text is bound to Node.data.key
new go.Binding("text", "key"))
);
myDiagram.linkTemplate =
$(go.Link,
{ toShortLength: 4, layoutConditions: go.Part.LayoutAdded | go.Part.LayoutRemoved, selectable: false },
$(go.Shape,
{ stroke: "black", strokeWidth: 3 },
new go.Binding("stroke", "isHighlighted", linkHighlightConverter).ofObject()
),
$(go.Shape,
{ toArrow: "standard", stroke: null, fill: "black" },
new go.Binding("fill", "isHighlighted", linkHighlightConverter).ofObject()
)
);
function linkHighlightConverter(h, shape) {
if (h) return "red";
return "black";
}
// create the model data that will be represented by Nodes and Links
myDiagram.model = new go.GraphLinksModel(
[
{ key: "Alpha", color: "lightblue" },
{ key: "Beta", color: "orange" },
{ key: "Gamma", color: "lightgreen" },
{ key: "Delta", color: "pink" },
{ key: "g1", isGroup: true },
{ key: "Omega", group: "g1" },
{ key: "Epsilon", group: "g1" }
],
[
{ from: "Alpha", to: "g1" },
{ from: "g1", to: "Beta" },
{ from: "Beta", to: "Gamma" },
{ from: "Beta", to: "Delta" },
{ from: "Omega", to: "Epsilon" }
]);
}
This Pen doesn't use any external CSS resources.