gojs
Version:
Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams
418 lines (390 loc) • 17.3 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="An editor for defining planograms: visual displays of merchandise."/>
<link rel="stylesheet" href="../assets/css/style.css"/>
<!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
<title>Planogram</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">
<style type="text/css">
/* This CSS is used to create the accordion for the Palettes */
input {
position: absolute;
opacity: 0;
z-index: -1;
}
/* Accordion styles */
.tabs {
overflow: hidden;
}
.tab {
width: 100%;
color: white;
overflow: hidden;
}
.tab-label {
display: flex;
justify-content: space-between;
padding: 0.5em;
background: #1F4963;
cursor: pointer;
}
.tab-label:hover {
background: #627f91;
}
.tab-label::after {
content: "❯";
width: 1em;
height: 1em;
text-align: center;
transition: all 0.35s;
}
.tab-content {
max-height: 0;
color: #2c3e50;
background: white;
}
.tab-close {
display: flex;
justify-content: flex-end;
padding: 1em;
font-size: 0.75em;
background: #2c3e50;
cursor: pointer;
}
.tab-close:hover {
background: #1a252f;
}
input:checked + .tab-label {
background: #1a252f;
}
input:checked + .tab-label::after {
transform: rotate(90deg);
}
input:checked ~ .tab-content {
max-height: 100vh;
}
</style>
<script id="code">
// General Parameters for this app, used during initialization
var AllowTopLevel = false;
var CellSize = new go.Size(50, 50);
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
// 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;
myDiagram =
new go.Diagram("myDiagramDiv",
{
grid: $(go.Panel, "Grid",
{ gridCellSize: CellSize },
$(go.Shape, "LineH", { stroke: "lightgray" }),
$(go.Shape, "LineV", { stroke: "lightgray" })
),
// support grid snapping when dragging and when resizing
"draggingTool.isGridSnapEnabled": true,
"draggingTool.gridSnapCellSpot": go.Spot.Center,
"resizingTool.isGridSnapEnabled": true,
// For this sample, automatically show the state of the diagram's model on the page
"ModelChanged": e => {
if (e.isTransactionFinished) {
document.getElementById("savedModel").textContent = myDiagram.model.toJson();
}
},
"animationManager.isEnabled": false,
"undoManager.isEnabled": true
});
// Regular Nodes represent items to be put onto racks.
// Nodes are currently resizable, but if that is not desired, just set resizable to false.
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
resizable: true, resizeObjectName: "SHAPE",
// because the gridSnapCellSpot is Center, offset the Node's location
locationSpot: new go.Spot(0, 0, CellSize.width / 2, CellSize.height / 2),
// provide a visual warning about dropping anything onto an "item"
mouseDragEnter: (e, node) => {
e.handled = true;
node.findObject("SHAPE").fill = "red";
e.diagram.currentCursor = "not-allowed";
highlightGroup(node.containingGroup, false);
},
mouseDragLeave: (e, node) => node.updateTargetBindings(),
// disallow dropping anything onto an "item"
mouseDrop: (e, node) => node.diagram.currentTool.doCancel()
},
// always save/load the point that is the top-left corner of the node, not the location
new go.Binding("position", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
// this is the primary thing people see
$(go.Shape, "Rectangle",
{
name: "SHAPE",
fill: "white",
minSize: CellSize,
desiredSize: CellSize // initially 1x1 cell
},
new go.Binding("fill", "color"),
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify)),
// with the textual key in the middle
$(go.TextBlock,
{ alignment: go.Spot.Center, font: 'bold 16px sans-serif' },
new go.Binding("text", "key"))
); // end Node
// Groups represent racks where items (Nodes) can be placed.
// Currently they are movable and resizable, but you can change that
// if you want the racks to remain "fixed".
// Groups provide feedback when the user drags nodes onto them.
function highlightGroup(grp, show) {
if (!grp) return false;
// check that the drop may really happen into the Group
var tool = grp.diagram.toolManager.draggingTool;
grp.isHighlighted = show && grp.canAddMembers(tool.draggingParts);
return grp.isHighlighted;
}
var groupFill = "rgba(128,128,128,0.2)";
var groupStroke = "gray";
var dropFill = "rgba(128,255,255,0.2)";
var dropStroke = "red";
myDiagram.groupTemplate =
$(go.Group,
{
layerName: "Background",
resizable: true, resizeObjectName: "SHAPE",
// because the gridSnapCellSpot is Center, offset the Group's location
locationSpot: new go.Spot(0, 0, CellSize.width / 2, CellSize.height / 2)
},
// always save/load the point that is the top-left corner of the node, not the location
new go.Binding("position", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
{ // what to do when a drag-over or a drag-drop occurs on a Group
mouseDragEnter: (e, grp, prev) => {
if (!highlightGroup(grp, true)) e.diagram.currentCursor = "not-allowed"; else e.diagram.currentCursor = "";
},
mouseDragLeave: (e, grp, next) => highlightGroup(grp, false),
mouseDrop: (e, grp) => {
var ok = grp.addMembers(grp.diagram.selection, true);
if (!ok) grp.diagram.currentTool.doCancel();
}
},
$(go.Shape, "Rectangle", // the rectangular shape around the members
{
name: "SHAPE",
fill: groupFill,
stroke: groupStroke,
minSize: new go.Size(CellSize.width * 2, CellSize.height * 2)
},
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
new go.Binding("fill", "isHighlighted", h => h ? dropFill : groupFill).ofObject(),
new go.Binding("stroke", "isHighlighted", h => h ? dropStroke : groupStroke).ofObject())
);
// decide what kinds of Parts can be added to a Group
myDiagram.commandHandler.memberValidation = (grp, node) => {
if (grp instanceof go.Group && node instanceof go.Group) return false; // cannot add Groups to Groups
// but dropping a Group onto the background is always OK
return true;
};
// what to do when a drag-drop occurs in the Diagram's background
myDiagram.mouseDragOver = e => {
if (!AllowTopLevel) {
// OK to drop a group anywhere or any Node that is a member of a dragged Group
var tool = e.diagram.toolManager.draggingTool;
if (!tool.draggingParts.all(p => p instanceof go.Group ||
(!p.isTopLevel && tool.draggingParts.contains(p.containingGroup)))) {
e.diagram.currentCursor = "not-allowed";
} else {
e.diagram.currentCursor = "";
}
}
};
myDiagram.mouseDrop = e => {
if (AllowTopLevel) {
// when the selection is dropped in the diagram's background,
// make sure the selected Parts no longer belong to any Group
if (!e.diagram.commandHandler.addTopLevelParts(e.diagram.selection, true)) {
e.diagram.currentTool.doCancel();
}
} else {
// disallow dropping any regular nodes onto the background, but allow dropping "racks",
// including any selected member nodes
if (!e.diagram.selection.all(p => {
return p instanceof go.Group || (!p.isTopLevel && p.containingGroup.isSelected);
})) {
e.diagram.currentTool.doCancel();
}
}
};
// start off with four "racks" that are positioned next to each other
myDiagram.model = new go.GraphLinksModel([
{ key: "G1", isGroup: true, pos: "0 0", size: "200 200" },
{ key: "G2", isGroup: true, pos: "200 0", size: "200 200" },
{ key: "G3", isGroup: true, pos: "0 200", size: "200 200" },
{ key: "G4", isGroup: true, pos: "200 200", size: "200 200" }
]);
// this sample does not make use of any links
// initialize the first Palette
myPaletteSmall =
new go.Palette("myPaletteSmall",
{ // share the templates with the main Diagram
nodeTemplate: myDiagram.nodeTemplate,
groupTemplate: myDiagram.groupTemplate
});
var green = '#B2FF59';
var blue = '#81D4FA';
var yellow = '#FFEB3B';
// specify the contents of the Palette
myPaletteSmall.model = new go.GraphLinksModel([
{ key: "g", color: green },
{ key: "b", color: blue },
{ key: "y", color: yellow }
]);
// initialize the second Palette, of tall items
myPaletteTall =
new go.Palette("myPaletteTall",
{ // share the templates with the main Diagram
nodeTemplate: myDiagram.nodeTemplate,
groupTemplate: myDiagram.groupTemplate
});
// specify the contents of the Palette
myPaletteTall.model = new go.GraphLinksModel([
{ key: "g", color: green, size: "50 100" },
{ key: "b", color: blue, size: "50 100" },
{ key: "y", color: yellow, size: "50 100" }
]);
// initialize the third Palette, of wide items
myPaletteWide =
new go.Palette("myPaletteWide",
{ // share the templates with the main Diagram
nodeTemplate: myDiagram.nodeTemplate,
groupTemplate: myDiagram.groupTemplate
});
// specify the contents of the Palette
myPaletteWide.model = new go.GraphLinksModel([
{ key: "g", color: green, size: "100 50" },
{ key: "b", color: blue, size: "100 50" },
{ key: "y", color: yellow, size: "100 50" }
]);
// initialize the fourth Palette, of big items
myPaletteBig =
new go.Palette("myPaletteBig",
{ // share the templates with the main Diagram
nodeTemplate: myDiagram.nodeTemplate,
groupTemplate: myDiagram.groupTemplate
});
// specify the contents of the Palette
myPaletteBig.model = new go.GraphLinksModel([
{ key: "g", color: green, size: "100 100" },
{ key: "b", color: blue, size: "100 100" },
{ key: "y", color: yellow, size: "100 100" }
]);
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div style="width: 135px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black">
<div class="tabs">
<div class="tab">
<input type="radio" id="rd1" name="rd" checked="true">
<label class="tab-label" for="rd1">Small items</label>
<div class="tab-content">
<div id="myPaletteSmall" style="width: 140px; height: 340px"></div>
</div>
</div>
<div class="tab">
<input type="radio" id="rd2" name="rd">
<label class="tab-label" for="rd2">Tall items</label>
<div class="tab-content">
<div id="myPaletteTall" style="width: 140px; height: 340px"></div>
</div>
</div>
<div class="tab">
<input type="radio" id="rd3" name="rd">
<label class="tab-label" for="rd3">Wide items</label>
<div class="tab-content">
<div id="myPaletteWide" style="width: 140px; height: 340px"></div>
</div>
</div>
<div class="tab">
<input type="radio" id="rd4" name="rd">
<label class="tab-label" for="rd4">Big items</label>
<div class="tab-content">
<div id="myPaletteBig" style="width: 140px; height: 340px"></div>
</div>
</div>
</div>
</div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 500px; border: solid 1px black"></div>
</div>
<p>
A <em>planogram</em> is a visual representation of a store's products or services, often used as a tool to maximize sales.
</p>
<p>
Drag-and-drop "items" from the Palette onto "racks" in the Diagram.
"Items" are implemented as Nodes; "racks" are implemented as Groups.
Once items have been placed on a rack, they can be resized, if necessary.
The <a>DraggingTool.isGridSnapEnabled</a> and <a>ResizingTool.isGridSnapEnabled</a> are both set
to true to allow for snapping to the background grid.
Node and Group templates both use dragging functions to allow for highlighting so the user knows which
rack an item will belong to.
</p>
<p>
A pure CSS Accordion is used to create the four collapsible Palettes.
</p>
<div>
Diagram Model saved in JSON format, automatically updated after each transaction:
<pre id="savedModel" style="height:250px"></pre>
</div>
<p>
See also Northwoods Software's planogramming services: <a href="https://goplanogram.com" target="_blank">GoPlanogram</a>.
</p>
</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>