gojs
Version:
Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams
289 lines (269 loc) • 14.4 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="Create new collapsible groups, ungroup existing groups, and use drag-and-drop to change grouping memberships."/>
<link rel="stylesheet" href="../assets/css/style.css"/>
<!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
<title>Regrouping Demo</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">
function init() {
myDiagram = new go.Diagram("myDiagramDiv",
{
// when a drag-drop occurs in the Diagram's background, make it a top-level node
mouseDrop: e => finishDrop(e, null),
layout: // Diagram has simple horizontal layout
new go.GridLayout(
{ wrappingWidth: Infinity, alignment: go.GridLayout.Position, cellSize: new go.Size(1, 1) }),
"commandHandler.archetypeGroupData": { isGroup: true, text: "Group", horiz: false },
"undoManager.isEnabled": true
});
// The one template for Groups can be configured to be either layout out its members
// horizontally or vertically, each with a different default color.
function makeLayout(horiz) { // a Binding conversion function
if (horiz) {
return new go.GridLayout(
{
wrappingWidth: Infinity, alignment: go.GridLayout.Position,
cellSize: new go.Size(1, 1), spacing: new go.Size(4, 4)
});
} else {
return new go.GridLayout(
{
wrappingColumn: 1, alignment: go.GridLayout.Position,
cellSize: new go.Size(1, 1), spacing: new go.Size(4, 4)
});
}
}
function defaultColor(horiz) { // a Binding conversion function
return horiz ? "rgba(255, 221, 51, 0.55)" : "rgba(51,211,229, 0.5)";
}
function defaultFont(horiz) { // a Binding conversion function
return horiz ? "bold 20px sans-serif" : "bold 16px sans-serif";
}
// this function is used to highlight a Group that the selection may be dropped into
function highlightGroup(e, grp, show) {
if (!grp) return;
e.handled = true;
if (show) {
// cannot depend on the grp.diagram.selection in the case of external drag-and-drops;
// instead depend on the DraggingTool.draggedParts or .copiedParts
var tool = grp.diagram.toolManager.draggingTool;
var map = tool.draggedParts || tool.copiedParts; // this is a Map
// now we can check to see if the Group will accept membership of the dragged Parts
if (grp.canAddMembers(map.toKeySet())) {
grp.isHighlighted = true;
return;
}
}
grp.isHighlighted = false;
}
// Upon a drop onto a Group, we try to add the selection as members of the Group.
// Upon a drop onto the background, or onto a top-level Node, make selection top-level.
// If this is OK, we're done; otherwise we cancel the operation to rollback everything.
function finishDrop(e, grp) {
var ok = (grp !== null
? grp.addMembers(grp.diagram.selection, true)
: e.diagram.commandHandler.addTopLevelParts(e.diagram.selection, true));
if (!ok) e.diagram.currentTool.doCancel();
}
myDiagram.groupTemplate =
new go.Group("Auto",
{
background: "blue",
ungroupable: true,
// highlight when dragging into the Group
mouseDragEnter: (e, grp, prev) => highlightGroup(e, grp, true),
mouseDragLeave: (e, grp, next) => highlightGroup(e, grp, false),
computesBoundsAfterDrag: true,
computesBoundsIncludingLocation: true,
// when the selection is dropped into a Group, add the selected Parts into that Group;
// if it fails, cancel the tool, rolling back any changes
mouseDrop: finishDrop,
handlesDragDropForMembers: true, // don't need to define handlers on member Nodes and Links
// Groups containing Groups lay out their members horizontally
layout: makeLayout(false)
})
.bind("layout", "horiz", makeLayout)
.bind(new go.Binding("background", "isHighlighted", h => h ? "rgba(255,0,0,0.2)" : "transparent").ofObject())
.add(new go.Shape("Rectangle",
{ fill: null, stroke: defaultColor(false), fill: defaultColor(false), strokeWidth: 2 })
.bind("stroke", "horiz", defaultColor)
.bind("fill", "horiz", defaultColor))
.add(
new go.Panel("Vertical") // title above Placeholder
.add(new go.Panel("Horizontal", // button next to TextBlock
{ stretch: go.GraphObject.Horizontal, background: defaultColor(false) })
.bind("background", "horiz", defaultColor)
.add(go.GraphObject.make("SubGraphExpanderButton", { alignment: go.Spot.Right, margin: 5 }))
.add(new go.TextBlock(
{
alignment: go.Spot.Left,
editable: true,
margin: 5,
font: defaultFont(false),
opacity: 0.95, // allow some color to show through
stroke: "#404040"
})
.bind("font", "horiz", defaultFont)
.bind("text", "text", null, null)) // `null` as the fourth argument makes this a two-way binding
) // end Horizontal Panel
.add(new go.Placeholder({ padding: 5, alignment: go.Spot.TopLeft }))
) // end Vertical Panel
myDiagram.nodeTemplate =
new go.Node("Auto",
{ // dropping on a Node is the same as dropping on its containing Group, even if it's top-level
mouseDrop: (e, node) => finishDrop(e, node.containingGroup)
})
.add(new go.Shape("RoundedRectangle", { fill: "rgba(172, 230, 0, 0.9)", stroke: "white", strokeWidth: 0.5 }))
.add(new go.TextBlock(
{
margin: 7,
editable: true,
font: "bold 13px sans-serif",
opacity: 0.90
})
.bind("text", "text", null, null)); // `null` as the fourth argument makes this a two-way binding
// initialize the Palette and its contents
myPalette =
new go.Palette("myPaletteDiv",
{
nodeTemplateMap: myDiagram.nodeTemplateMap,
groupTemplateMap: myDiagram.groupTemplateMap
});
myPalette.model = new go.GraphLinksModel([
{ text: "New Node", color: "#ACE600" },
{ isGroup: true, text: "H Group", horiz: true },
{ isGroup: true, text: "V Group", horiz: false }
]);
var slider = document.getElementById("levelSlider");
slider.addEventListener('change', reexpand);
slider.addEventListener('input', reexpand);
load();
}
function reexpand(e) {
myDiagram.commit(diag => {
var level = parseInt(document.getElementById("levelSlider").value);
diag.findTopLevelGroups().each(g => expandGroups(g, 0, level))
} ,"reexpand");
}
function expandGroups(g, i, level) {
if (!(g instanceof go.Group)) return;
g.isSubGraphExpanded = i < level;
g.memberParts.each(m => expandGroups(m, i + 1, level))
}
// save a model to and load a model from JSON text, displayed below the Diagram
function save() {
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div id="myPaletteDiv" style="width: 130px; margin-right: 2px; background-color: rgb(39, 29, 29); border: solid 1px black"></div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 500px; background-color: rgb(39, 29, 29); border: solid 1px black"></div>
</div>
<p>
This sample allows the user to drag nodes, including groups, into and out of groups,
both from the Palette as well as from within the Diagram.
See the <a href="../intro/groups.html">Groups Intro page</a> for an explanation of GoJS Groups.
</p>
<p>
Highlighting to show feedback about potential addition to a group during a drag is implemented
using <a>GraphObject.mouseDragEnter</a> and <a>GraphObject.mouseDragLeave</a> event handlers.
Because <a>Group.computesBoundsAfterDrag</a> is true, the Group's <a>Placeholder</a>'s bounds are
not computed until the drop happens, so the group does not continuously expand as the user drags
a member of a group.
</p>
<p>
When a drop occurs on a Group or a regular Node, the <a>GraphObject.mouseDrop</a> event handler
adds the selection (the dragged Nodes) to the Group as new members.
The <a>Diagram.mouseDrop</a> event handler changes the dragged selected Nodes to be top-level,
rather than members of whatever Groups they had been in.
</p>
<p>
The slider controls how many nested levels of Groups are expanded. </br>
Semantic zoom level: <input id="levelSlider" type="range" min="0" max="5" value="3" />
</p>
<div id="buttons">
<button id="saveModel" onclick="save()">Save</button>
<button id="loadModel" onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"nodeDataArray": [
{"key":1, "isGroup":true, "text":"Main 1", "horiz":true},
{"key":2, "isGroup":true, "text":"Main 2", "horiz":true},
{"key":3, "isGroup":true, "text":"Group A", "group":1},
{"key":4, "isGroup":true, "text":"Group B", "group":1},
{"key":5, "isGroup":true, "text":"Group C", "group":2},
{"key":6, "isGroup":true, "text":"Group D", "group":2},
{"key":7, "isGroup":true, "text":"Group E", "group":6},
{"text":"first A", "group":3, "key":-7},
{"text":"second A", "group":3, "key":-8},
{"text":"first B", "group":4, "key":-9},
{"text":"second B", "group":4, "key":-10},
{"text":"third B", "group":4, "key":-11},
{"text":"first C", "group":5, "key":-12},
{"text":"second C", "group":5, "key":-13},
{"text":"first D", "group":6, "key":-14},
{"text":"first E", "group":7, "key":-15}
],
"linkDataArray": [ ]}
</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>