gojs
Version:
Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams
286 lines (267 loc) • 13.8 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="Drag and drop items between nodes."/>
<link rel="stylesheet" href="../assets/css/style.css"/>
<!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
<title>Dragging Fields Between Records</title>
</head>
<body>
<!-- This top nav is not part of the sample code -->
<nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
<div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
<div class="md:pl-4">
<a class="text-white hover:text-white no-underline hover:no-underline
font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
<h1 class="my-0 p-1 ">GoJS</h1>
</a>
</div>
<button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
<svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
<path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
<path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
</svg>
</button>
<div id="topnavList" class="hidden sm:block items-center w-auto mt-0 text-white p-0 z-20">
<ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
<li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
</ul>
</div>
</div>
<hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
</nav>
<div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
<div id="navSide" class="flex flex-col w-full md:w-48 text-gray-700 bg-white flex-shrink-0"></div>
<!-- * * * * * * * * * * * * * -->
<!-- Start of GoJS sample code -->
<script src="../release/go.js"></script>
<div id="allSampleContent" class="p-4 w-full">
<script id="code">
// Custom DraggingTool for dragging fields instead of whole Parts.
// FieldDraggingTool.fieldTemplate needs to be set to a template of the field that you want shown while dragging.
class FieldDraggingTool extends go.DraggingTool {
constructor() {
super();
this.fieldTemplate = null; // THIS NEEDS TO BE SET before a drag starts
this.temporaryPart = null;
}
// override this method
findDraggablePart() {
const diagram = this.diagram;
let obj = diagram.findObjectAt(diagram.lastInput.documentPoint);
while (obj !== null && obj.type !== go.Panel.TableRow) obj = obj.panel;
if (obj !== null && obj.type === go.Panel.TableRow &&
this.fieldTemplate !== null && this.temporaryPart === null) {
const tempPart =
go.GraphObject.make(go.Node, "Table",
{ layerName: "Tool", locationSpot: go.Spot.Center },
this.fieldTemplate.copy()); // copy the template!
this.temporaryPart = tempPart;
// assume OBJ is now a Panel representing a field, bound to field data
// update the temporary Part via data binding
tempPart.location = diagram.lastInput.documentPoint; // need to set location explicitly
diagram.add(tempPart); // add to Diagram before setting data
tempPart.data = obj.data; // bind to the same field data as being dragged
return tempPart;
}
return super.findDraggablePart();
}
doActivate() {
if (this.temporaryPart === null) return super.doActivate();
const diagram = this.diagram;
this.standardMouseSelect();
this.isActive = true;
// instead of the usual result of computeEffectiveCollection, just use the temporaryPart alone
const map = new go.Map(/*go.Part, go.DraggingInfo*/);
map.set(this.temporaryPart, new go.DraggingInfo(diagram.lastInput.documentPoint.copy()));
this.draggedParts = map;
this.startTransaction("Drag Field");
diagram.isMouseCaptured = true;
}
doDeactivate() {
if (this.temporaryPart === null) return super.doDeactivate();
const diagram = this.diagram;
// make sure the temporary Part is no longer in the Diagram
diagram.remove(this.temporaryPart);
this.temporaryPart = null;
// now do all the standard deactivation cleanup,
// including setting isActive = false, clearing out draggedParts, calling stopTransaction(),
// and setting diagram.isMouseCaptured = false
super.doDeactivate();
}
doMouseMove() {
if (!this.isActive) return;
if (this.temporaryPart === null) return super.doMouseMove();
const diagram = this.diagram;
// just move the temporaryPart (in draggedParts), without regard to moving or copying permissions of the Node
const offset = diagram.lastInput.documentPoint.copy().subtract(diagram.firstInput.documentPoint);
this.moveParts(this.draggedParts, offset, false);
}
doMouseUp() {
if (!this.isActive) return;
if (this.temporaryPart === null) return super.doMouseUp();
const diagram = this.diagram;
const data = this.temporaryPart.data;
const dest = diagram.findPartAt(diagram.lastInput.documentPoint, false);
if (dest !== null && dest.data && dest.data.fields) {
const panel = dest.findObject("TABLE");
const idx = panel.findRowForLocalY(panel.getLocalPoint(diagram.lastInput.documentPoint).y);
diagram.model.insertArrayItem(dest.data.fields, idx + 1,
{ name: data.name, info: data.info, color: data.color, figure: data.figure });
}
const src = this.currentPart;
// whether or not there was a destination node, delete the original field
if (!(diagram.lastInput.control || diagram.lastInput.meta)) {
const sidx = src.data.fields.indexOf(data);
if (sidx >= 0) {
diagram.model.removeArrayItem(src.data.fields, sidx);
}
}
this.transactionResult = "Inserted Field";
this.stopTool();
}
}
// end of FieldDraggingTool
function init() {
// Since 2.2 you can also author concise templates with method chaining instead of GraphObject.make
// For details, see https://gojs.net/latest/intro/buildingObjects.html
const $ = go.GraphObject.make; // for conciseness in defining templates
myDiagram =
new go.Diagram("myDiagramDiv",
{
validCycle: go.Diagram.CycleNotDirected, // don't allow loops
draggingTool: $(FieldDraggingTool), // use custom DraggingTool
// automatically update the model that is shown on this page
"ModelChanged": e => { if (e.isTransactionFinished) showModel(); },
"undoManager.isEnabled": true
});
// This template is a Panel that is used to represent each item in a Panel.itemArray.
// The Panel is data bound to the item object.
// This template needs to be used by the FieldDraggingTool as well as the Diagram.nodeTemplate.
const fieldTemplate =
$(go.Panel, "TableRow", // this Panel is a row in the containing Table
new go.Binding("portId", "name"), // this Panel is a "port"
{
background: "transparent", // so this port's background can be picked by the mouse
fromSpot: go.Spot.Right, // links only go from the right side to the left side
toSpot: go.Spot.Left
}, // allow drawing links from or to this port
$(go.Shape,
{ width: 12, height: 12, column: 0, strokeWidth: 2, margin: 4 },
new go.Binding("figure", "figure"),
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: new go.Margin(0, 2), column: 1, font: "bold 13px sans-serif" },
new go.Binding("text", "name")),
$(go.TextBlock,
{ margin: new go.Margin(0, 2), column: 2, font: "13px sans-serif" },
new go.Binding("text", "info"))
);
// the FieldDraggingTool needs a template for what to show while dragging
myDiagram.toolManager.draggingTool.fieldTemplate = fieldTemplate;
// This template represents a whole "record".
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
movable: false,
copyable: false,
deletable: false,
locationSpot: go.Spot.Center
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
// this rectangular shape surrounds the content of the node
$(go.Shape,
{ fill: "#EEEEEE" }),
// the content consists of a header and a list of items
$(go.Panel, "Vertical",
// this is the header for the whole node
$(go.Panel, "Auto",
{ stretch: go.GraphObject.Horizontal }, // as wide as the whole node
$(go.Shape,
{ fill: "#1570A6", stroke: null }),
$(go.TextBlock,
{
alignment: go.Spot.Center,
margin: 3,
stroke: "white",
textAlign: "center",
font: "bold 12pt sans-serif"
},
new go.Binding("text", "title"))),
// this Panel holds a Panel for each item object in the itemArray;
// each item Panel is defined by the itemTemplate to be a TableRow in this Table
$(go.Panel, "Table",
{
name: "TABLE",
padding: 2,
minSize: new go.Size(100, 10),
defaultStretch: go.GraphObject.Horizontal,
itemTemplate: fieldTemplate
},
new go.Binding("itemArray", "fields")
) // end Table Panel of items
) // end Vertical Panel
); // end Node
myDiagram.model =
new go.GraphLinksModel(
{
linkFromPortIdProperty: "fromPort",
linkToPortIdProperty: "toPort",
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray: [
{
key: 1,
title: "Record1",
fields: [
{ name: "field1", info: "", color: "#F7B84B", figure: "Ellipse" },
{ name: "field2", info: "the second one", color: "#F25022", figure: "Ellipse" },
{ name: "fieldThree", info: "3rd", color: "#00BCF2" }
],
loc: "0 0"
},
{
key: 2,
title: "Record2",
fields: [
{ name: "fieldA", info: "", color: "#FFB900", figure: "Diamond" }
],
loc: "250 0"
}
]
});
showModel(); // show the diagram's initial model
function showModel() {
document.getElementById("mySavedModel").textContent = myDiagram.model.toJson();
}
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:300px"></div>
<p>Drag fields between records to move them; dragging within the same node can reorder them.
Fields can be copied when holding down the control key; they are deleted when dropped in the diagram's background.
The "record" Nodes are not movable or copyable or deletable.</p>
<p>The model data, automatically updated after each change or undo or redo:</p>
<textarea id="mySavedModel" style="width:100%;height:300px"></textarea>
</div>
</div>
<!-- * * * * * * * * * * * * * -->
<!-- End of GoJS sample code -->
</div>
</body>
<!-- This script is part of the gojs.net website, and is not needed to run the sample -->
<script src="../assets/js/goSamples.js"></script>
</html>