UNPKG

create-gojs-kit

Version:

A CLI for downloading GoJS samples, extensions, and docs

390 lines (350 loc) 16.1 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="A diagram that shows a sheet of paper; nodes cannot be dragged or resized beyond the edge of the sheet." /> <meta itemprop="description" content="A diagram that shows a sheet of paper; nodes cannot be dragged or resized beyond the edge of the sheet." /> <meta property="og:description" content="A diagram that shows a sheet of paper; nodes cannot be dragged or resized beyond the edge of the sheet." /> <meta name="twitter:description" content="A diagram that shows a sheet of paper; nodes cannot be dragged or resized beyond the edge of the sheet." /> <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="Single Page Diagram Editor with Legend" /> <meta property="og:title" content="Single Page Diagram Editor with Legend" /> <meta name="twitter:title" content="Single Page Diagram Editor with Legend" /> <meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/singlepage.png" /> <meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/singlepage.png" /> <meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/singlepage.png" /> <meta property="og:url" content="https://gojs.net/latest/samples/singlePage.html" /> <meta property="twitter:url" content="https://gojs.net/latest/samples/singlePage.html" /> <meta name="twitter:card" content="summary_large_image" /> <meta property="og:type" content="website" /> <meta property="twitter:domain" content="gojs.net" /> <title> Single Page Diagram Editor with Legend | 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"> <div id="sample"> <div id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 650px"></div> <p>This pretends to show a sheet of paper with the diagram on it.</p> <p> Both the <a>DraggingTool</a> and the <a>ResizingTool</a> are constrained to keep the nodes within the area of the sheet of paper, minus the margins. The user can zoom and scroll/pan normally. There are several variables, such as <code>pageSize</code>, that govern how the sheet of paper is set up. </p> <p> The Legend is implemented with a template that is assumed to only be used for a single Part. It is located at the bottom right corner of the usable area. Its text can be edited, and the legend adapts in size automatically. </p> </div> <script id="code"> const pageSize = new go.Size(792, 612); // landscape, or new go.Size(612, 792) for portrait const pageMargin = new go.Margin(8); // margin around the sheet const usableMargin = new go.Margin(16); // padding inside sheet border const pageBounds = new go.Rect(-usableMargin.left, -usableMargin.top, pageSize.width, pageSize.height); const usableArea = pageBounds.copy().subtractMargin(usableMargin); myDiagram = new go.Diagram('myDiagramDiv', { fixedBounds: pageBounds.copy().addMargin(pageMargin), initialAutoScale: go.AutoScale.Uniform, 'animationManager.isInitial': false, 'undoManager.isEnabled': true, 'draggingTool.isGridSnapEnabled': true, 'draggingTool.gridSnapCellSize': new go.Size(10, 10), 'resizingTool.isGridSnapEnabled': true, 'resizingTool.cellSize': new go.Size(10, 10), // override ResizingTool methods to limit where the resizing may go 'resizingTool.doMouseMove': function () { // method override must be function, not => var e = this.diagram.lastInput; e.documentPoint = limitPoint(e.documentPoint); e.viewPoint = this.diagram.transformDocToView(e.documentPoint); go.ResizingTool.prototype.doMouseMove.call(this); }, 'resizingTool.doMouseUp': function () { // method override must be function, not => var e = this.diagram.lastInput; e.documentPoint = limitPoint(e.documentPoint); e.viewPoint = this.diagram.transformDocToView(e.documentPoint); go.ResizingTool.prototype.doMouseUp.call(this); }, // if text has changed, maybe the node bounds has changed too TextEdited: e => { var node = e.subject.part; node.ensureBounds(); // has been resized, compute its new bounds var pt = limitPoint(node.location); node.location = stayInFixedArea(node, pt, pt); } }); // The background Part showing the sheet of paper; // it includes the fixed bounds of the diagram contents myDiagram.add( new go.Part('Grid', { layerName: 'Grid', position: pageBounds.position, desiredSize: pageSize, isShadowed: true, background: 'floralwhite' }) .add( new go.Shape('LineH', { stroke: 'lightgray', strokeWidth: 0.5 }), new go.Shape('LineV', { stroke: 'lightgray', strokeWidth: 0.5 }) ) ); // This function is the Node.dragComputation, to limit the movement of the parts. function stayInFixedArea(part, pt, gridpt) { const diagram = part.diagram; if (diagram === null) return pt; // compute the document area without padding const v = usableArea; // get the bounds of the part being dragged const bnd = part.actualBounds; const loc = part.location; // now limit the location appropriately const l = v.x + (loc.x - bnd.x); const r = v.right - (bnd.right - loc.x); const t = v.y + (loc.y - bnd.y); const b = v.bottom - (bnd.bottom - loc.y); if (l <= gridpt.x && gridpt.x <= r && t <= gridpt.y && gridpt.y <= b) return gridpt; const p = gridpt.copy(); if (diagram.toolManager.draggingTool.isGridSnapEnabled) { // find a location that is inside V but also keeps the part's bounds within V const cw = diagram.grid.gridCellSize.width; if (cw > 0) { while (p.x > r) p.x -= cw; while (p.x < l) p.x += cw; } const ch = diagram.grid.gridCellSize.height; if (ch > 0) { while (p.y > b) p.y -= ch; while (p.y < t) p.y += ch; } return p; } else { p.x = Math.max(l, Math.min(p.x, r)); p.y = Math.max(t, Math.min(p.y, b)); return p; } } function limitPoint(p) { return new go.Point(Math.max(usableArea.left, Math.min(p.x, usableArea.right)), Math.max(usableArea.top, Math.min(p.y, usableArea.bottom))); } myDiagram.nodeTemplate = new go.Node('Auto', { resizable: true, // but limited by overrides of ResizingTool methods, above dragComputation: stayInFixedArea // this limits the DraggingTool }) .add( new go.Shape({ fill: 'white', portId: '', fromLinkable: true, toLinkable: true, cursor: 'pointer' }) .bind('fill', 'color'), new go.TextBlock({ margin: 8, editable: true }) .bindTwoWay('text') ); myDiagram.linkTemplate = new go.Link({ relinkableFrom: true, relinkableTo: true }) .add( new go.Shape(), new go.Shape({ toArrow: 'OpenTriangle' }) ); // The Legend Part, presumably a singleton function makeField(name, propname, row, col, span) { if (!span) span = 1; return [ new go.TextBlock( name, { row: row, column: col, columnSpan: span, alignment: go.Spot.TopLeft, margin: new go.Margin(2, 0, 16, 2), stroke: 'gray', font: '9pt sans-serif', isMultiline: false }), new go.TextBlock({ row: row, column: col, columnSpan: span, alignment: go.Spot.Center, margin: new go.Margin(16, 4, 1, 4), textAlign: 'center', editable: true }) .bindTwoWay('text', propname) ]; } function makeSeparator(row, col) { return new go.Shape('LineV', { row: row, column: col, width: 0, stretch: go.Stretch.Vertical, stroke: 'gray' }); } myDiagram.nodeTemplateMap.add('Legend', new go.Part('Auto', { layerName: 'Background', locationSpot: go.Spot.BottomRight, location: new go.Point().setRectSpot(usableArea, go.Spot.BottomRight), deletable: false, movable: false, copyable: false, selectionAdorned: false }) .add( new go.Shape({ fill: 'white', stroke: 'gray' }), new go.Panel('Table', { defaultRowSeparatorStroke: 'gray' }) .add( ...makeField('Title:', 'title', 0, 0, 5), ...makeField('Project:', 'name', 1, 0, 3), makeSeparator(1, 3), ...makeField('Number:', 'number', 1, 4) ) ) ); myDiagram.model = new go.GraphLinksModel( [ { category: 'Legend', title: 'Sample Fixed Diagram', name: 'Your Company', number: '123-45a' }, { key: 1, text: 'Alpha', color: 'lightblue' }, { key: 2, text: 'Beta', color: 'orange' }, { key: 3, text: 'Gamma', color: 'lightgreen' }, { key: 4, text: 'Delta', color: 'pink' } ], [ { from: 1, to: 2 }, { from: 1, to: 3 }, { from: 2, to: 2 }, { from: 3, to: 4 }, { from: 4, to: 1 } ] ); </script> </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>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>Legend</h4> <p> A Legend can be created for a Diagram using a simple <a>Part</a>. Typically that is added directly to the Diagram as an unmodeled Part, not as a template with data in the Model. However you may want to define a template and add a legend data object to the model so that you can parameterize the legend with information persisted with the model. </p> <p> Usually a legend will be created as an "Auto" Panel for a border around a "Table" Panel holding information about the types of nodes and/or links that are in the diagram. </p> <p> It probably will want to be in the "ViewportBackground" or "ViewportForeground" Layer so that it is always visible in the viewport despite scrolling or zooming. Set the <a>GraphObject.alignment</a> property to position it where you want it to be; by default it will be in the lower right corner. However you may want to treat the legend Part as a regular Part in the Diagram, possibly laid out by the diagram's <a>Diagram.layout</a>. </p> <p> More information can be found in the <a href="../intro/legends.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#legend">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>