UNPKG

create-gojs-kit

Version:

A CLI for downloading GoJS samples, extensions, and docs

603 lines (544 loc) 25.9 kB
<!DOCTYPE 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." /> <meta itemprop="description" content="An editor for defining planograms: visual displays of merchandise." /> <meta property="og:description" content="An editor for defining planograms: visual displays of merchandise." /> <meta name="twitter:description" content="An editor for defining planograms: visual displays of merchandise." /> <link rel="preconnect" href="https://rsms.me/"> <link rel="stylesheet" href="../assets/css/style.css"> <!-- Copyright 1998-2025 by Northwoods Software Corporation. --> <meta itemprop="name" content="Planogram Editor: Drag-and-Drop items onto Shelves and Racks" /> <meta property="og:title" content="Planogram Editor: Drag-and-Drop items onto Shelves and Racks" /> <meta name="twitter:title" content="Planogram Editor: Drag-and-Drop items onto Shelves and Racks" /> <meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/planogram.png" /> <meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/planogram.png" /> <meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/planogram.png" /> <meta property="og:url" content="https://gojs.net/latest/samples/planogram.html" /> <meta property="twitter:url" content="https://gojs.net/latest/samples/planogram.html" /> <meta name="twitter:card" content="summary_large_image" /> <meta property="og:type" content="website" /> <meta property="twitter:domain" content="gojs.net" /> <title> Planogram Editor: Drag-and-Drop items onto Shelves and Racks | GoJS Diagramming Library </title> </head> <body> <!-- This top nav is not part of the sample code --> <nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200"> <div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4"> <a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo" href="../"> GoJS </a> <div class="relative"> <button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation"> <svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> <div id="topnavList" class="hidden md:block"> <div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit"> <a href="../learn/">Learn</a> <a href="../samples/">Samples</a> <a href="../intro/">Intro</a> <a href="../api/">API</a> <a href="../download.html">Download</a> <a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a> <a id="tc" href="https://nwoods.com/contact.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a> <a id="tb" href="https://nwoods.com/sales/index.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a> </div> </div> </div> </div> </nav> <script> window.addEventListener("DOMContentLoaded", function () { // topnav var topButton = document.getElementById("topnavButton"); var topnavList = document.getElementById("topnavList"); if (topButton && topnavList) { topButton.addEventListener("click", function (e) { topnavList .classList .toggle("hidden"); e.stopPropagation(); }); document.addEventListener("click", function (e) { // if the clicked element isn't the list, close the list if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) { topButton.click(); } }); // set active <a> element var url = window .location .href .toLowerCase(); var aTags = topnavList.getElementsByTagName('a'); for (var i = 0; i < aTags.length; i++) { var lowerhref = aTags[i] .href .toLowerCase(); if (lowerhref.endsWith('.html')) lowerhref = lowerhref.slice(0, -5); if (url.startsWith(lowerhref)) { aTags[i] .classList .add('active'); break; } } } }); </script> <div class="flex flex-col prose"> <div class="w-full max-w-screen-xl mx-auto"> <!-- * * * * * * * * * * * * * --> <!-- Start of GoJS sample code --> <script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></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() { myDiagram = new go.Diagram('myDiagramDiv', { grid: new go.Panel('Grid', { gridCellSize: CellSize }) .add( new go.Shape('LineH', { stroke: 'lightgray' }), new 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 = new 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 .bindTwoWay('position', 'pos', go.Point.parse, go.Point.stringify) // this is the primary thing people see .add( new go.Shape('Rectangle', { name: 'SHAPE', fill: 'white', minSize: CellSize, desiredSize: CellSize // initially 1x1 cell }) .bind('fill', 'color') .bindTwoWay('desiredSize', 'size', go.Size.parse, go.Size.stringify), // with the textual key in the middle new go.TextBlock({ alignment: go.Spot.Center, font: 'bold 16px sans-serif' }) .bind('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 = new 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), // 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(); } }) // always save/load the point that is the top-left corner of the node, not the location .bindTwoWay('position', 'pos', go.Point.parse, go.Point.stringify) .add( new 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) }) .bindTwoWay('desiredSize', 'size', go.Size.parse, go.Size.stringify) .bindObject('fill', 'isHighlighted', h => h ? dropFill : groupFill) .bindObject('stroke', 'isHighlighted', h => h ? dropStroke : groupStroke) ); // 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.has(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> <div id="allTagDescriptions" class="p-4 w-full max-w-screen-xl mx-auto"> <hr/> <h3 class="text-xl">GoJS Features in this sample</h3> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Groups</h4> <p> The <a href="../api/symbols/Group.html" target="api">Group</a> class is used to treat a collection of <a href="../api/symbols/Node.html" target="api">Node</a>s and <a href="../api/symbols/Link.html" target="api">Link</a>s as if they were a single <a href="../api/symbols/Node.html" target="api">Node</a>. Those nodes and links are members of the group; together they constitute a subgraph. </p> <p> A subgraph is <em>not</em> another <a href="../api/symbols/Diagram.html" target="api">Diagram</a>, so there is no separate HTML Div element for the subgraph of a group. All of the <a href="../api/symbols/Part.html" target="api">Part</a>s that are members of a <a href="../api/symbols/Group.html" target="api">Group</a> belong to the same Diagram as the Group. There can be links between member nodes and nodes outside of the group as well as links between the group itself and other nodes. There can even be links between member nodes and the containing group itself. </p> <p> More information can be found in the <a href="../intro/groups.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#groups">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Palette</h4> <p> A <a href="../api/symbols/Palette.html" target="api">Palette</a> is a subclass of <a href="../api/symbols/Diagram.html" target="api">Diagram</a> that is used to display a number of <a href="../api/symbols/Part.html" target="api">Part</a>s that can be dragged into the diagram that is being modified by the user. The initialization of a <a href="../api/symbols/Palette.html" target="api">Palette</a> is just like the initialization of any <a href="../api/symbols/Diagram.html" target="api">Diagram</a>. Like Diagrams, you can have more than one Palette on the page at the same time. </p> <p> More information can be found in the <a href="../intro/palette.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#palette">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Grid Patterns</h4> <p> <b>GoJS</b> provides functionality to display a grid of lines drawn at regular intervals. Grid Panels can also force dragged parts to be aligned on grid points, and resize parts to be multiples of the grid cell size. </p> <p> Grids are implemented using a type of <a href="../api/symbols/Panel.html" target="api">Panel</a>, <a href="../api/symbols/PanelLayout.html#Grid" target="api">Panel.Grid</a>. Grid Panels, like most other types of Panels, can be used within <a href="../api/symbols/Node.html" target="api">Node</a>s or any other kind of <a href="../api/symbols/Part.html" target="api">Part</a>. However when they are used as the <a href="../api/symbols/Diagram.html#grid" target="api">Diagram.grid</a>, they are effectively infinite in extent. </p> <p> More information can be found in the <a href="../intro/grids.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#grid">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>GoJS on Different Platforms</h4> <p> GoJS is intended to run in any environment that executes JavaScript. This includes on browsers and within browser frameworks, and also in headless contexts such as Node.js. </p> <p> We maintain examples for common frameworks: </p> <ul> <li> <strong>React:</strong> We provide a <a href="https://github.com/NorthwoodsSoftware/gojs-react" target="_blank" rel="noopener">React Component</a> as an <a href="https://npmjs.com/gojs-react" target="_blank" rel="noopener">NPM package</a> and a usage <a href="https://github.com/NorthwoodsSoftware/gojs-react-basic" target="_blank" rel="noopener">sample</a>. See the intro page <a href="../intro/react.html">Using GoJS with React</a> for more information on React integration. </li> <li> <strong>Angular:</strong> We provide an <a href="https://github.com/NorthwoodsSoftware/gojs-angular" target="_blank" rel="noopener">Angular Component</a> as an <a href="https://npmjs.com/gojs-angular" target="_blank" rel="noopener">NPM package</a> and a usage <a href="https://github.com/NorthwoodsSoftware/gojs-angular-basic" target="_blank" rel="noopener">sample</a>. See the intro page <a href="../intro/angular.html">Using GoJS with Angular</a> for more information on Angular integration. </li> <li> <strong>Vue:</strong> We provide a <a href="../samples/vue.html">Vue.js Sample</a>. </li> </ul> <p> More information, including usage on frameworks including Electron, Blazor, and node.js, can be found at the <a href="../intro/platforms.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#frameworks">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Commands</h4> <p> A <a href="../api/symbols/CommandHandler.html" target="api">CommandHandler</a> handles all default keyboard input events in a Diagram. There are many predefined methods on <a>CommandHandler</a> that implement common commands to operate on the Diagram or the current <a>Diagram.selection></a>. </p> <p> You can override <a>CommandHandler.doKeyDown</a> to handle additional keyboard shortcuts or to change which commands are invoked via the keyboard. <p> Your code can invoke a command by calling the appropriate method on the <a>Diagram.commandHandler</a>. Each command method has a corresponding <b>can...</b> predicate that your code can use to enable or disable any buttons that invoke the command. Your code can customize the behavior of a command by overriding the method on <a>CommandHandler</a>, or by setting properties on the <a>CommandHandler</a> or <a>Diagram</a> or <a>Part</a>s -- see <a href="../intro/permissions.html">GoJS Permissions</a>. </p> <p> There are several CommandHandler extensions that provide additional functionality. <p> More information can be found in the <a href="../intro/commands.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#commands">Related samples</a> </p> <hr> </div> </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>