Appearance
Draw your graph
This example shows how to edit your graph interractively.
Drag images on the graph canvas to add more nodes, when the toggle is in "Connect links" mode it's possible to wire the network. To remove an element from the network use the keyboard keys "del" or "backspace". Icons byIcons8
js
import Ogma from '@linkurious/ogma';
const placeholder = document.createElement('span');
document.body.appendChild(placeholder);
placeholder.style.visibility = 'hidden';
// helper routine to get the icon HEX code
export function getIconCode(className) {
placeholder.className = `icon-${className}`;
const code = getComputedStyle(placeholder, ':before').content;
return code[1];
}
// stop mobile browsers to scroll on touchstart
// Note: this demo uses the DragDropTouch polyfill for mobile browsers.
// Removed it if you do not require it.
document.addEventListener(
'touchmove',
e => {
e.preventDefault();
},
{ passive: false }
);
const nodeIcons = {
money: getIconCode('banknote'),
commodity: getIconCode('puzzle'),
documents: getIconCode('files'),
fraudster: getIconCode('cctv'),
office: getIconCode('building')
};
const nodeColors = {
money: '#56B956',
commodity: '#BD8362',
documents: '#CACACA',
fraudster: '#F7102F',
office: '#C7C7C7'
};
const createNode = (id, label, x, y) => {
counter++;
return {
id: id + counter,
attributes: {
icon: {
font: 'lucide',
color: nodeColors[id],
content: nodeIcons[id],
scale: 0.5
},
x: x,
y: y
},
data: { type: id }
};
};
// use this for the ID generation
let counter = 0;
// compute the toolbar size
const toolbarHeight = document.querySelector('.toolbar').clientHeight;
// start with a couple of nodes
const graph = {
nodes: [
createNode('money', '$ 3.000', 0, 0),
createNode('commodity', 'Assets', 45, 4)
],
edges: [
{
id: 'money-assets',
source: 'money1',
target: 'commodity2',
attributes: { shape: { head: 'arrow' } }
}
]
};
// start with an empty graph
const ogma = new Ogma({
graph: graph,
container: 'graph-container'
});
ogma.styles.setSelectedNodeAttributes({
color: false,
outline: false,
outerStroke: {
color: 'red'
}
});
ogma.styles.setHoveredEdgeAttributes({
color: '#ddd',
width: 0.8
});
ogma.styles.setSelectedEdgeAttributes({
color: '#ddd',
width: 0.8
});
ogma.styles.setHoveredNodeAttributes({
color: false,
outline: false,
outerStroke: {
color: '#ddd',
width: 2
}
});
ogma.styles.addNodeRule({
radius: 5,
color: 'white',
text: {
content: node => node.getData('type'),
font: 'Roboto'
},
innerStroke: {
color: '#ddd',
width: 1
}
});
ogma.styles.addEdgeRule({
color: '#ddd',
width: 0.4
});
// now control the drag and drop behaviour
const icons = document.querySelectorAll('[draggable="true"]');
for (let i = 0; i < icons.length; i++) {
icons[i].addEventListener('dragstart', ev => {
ev.dataTransfer.setData('type', ev.target.id);
});
}
const ogmaContainer = document.querySelector('#graph-container');
// prevent the browser to do anything else than drag the image
ogmaContainer.addEventListener('dragover', ev => {
ev.preventDefault();
});
ogmaContainer.addEventListener('drop', ev => {
// Convert the drop point to graph coords
const pos = ogma.view.screenToGraphCoordinates({
x: ev.clientX,
y: ev.clientY - toolbarHeight
});
// now get the icons type and its URL
const id = ev.dataTransfer.getData('type');
ev.dataTransfer.dropEffect = 'copy';
ev.preventDefault();
// create a node on the graph to the exact x and y of the drop
ogma.addNode(createNode(id, id, pos.x, pos.y));
ogma.view.locateGraph({ duration: 500 });
});
ogma.events.on('dragStart', () => {
if (getMode('drag-action') === 'links') {
ogma.tools.connectNodes.enable({
strokeColor: 'red',
createNodes: false,
// avoid self edges
condition: (source, target) => source.getId() !== target.getId(),
createEdge: edge => {
edge.attributes = { shape: { head: 'arrow' } };
return edge;
}
});
}
});
const deleteItems = () => {
const selectedNodes = ogma.getSelectedNodes();
const selectedEdges = ogma.getSelectedEdges();
if (selectedNodes || selectedEdges) {
if (selectedNodes) {
ogma.removeNodes(selectedNodes);
}
if (selectedEdges) {
ogma.removeEdges(selectedEdges);
}
}
};
// Enable the delete action on keypress
ogma.events.onKeyPress('del', deleteItems);
ogma.events.onKeyPress('backspace', deleteItems);
const getMode = id => {
const form = document.querySelector('form');
const select = form[id];
const currentMode = Array.prototype.filter.call(select, input => {
return input.checked;
})[0].value; // IE inconsistency
return currentMode;
};
html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/solid.min.css"
rel="stylesheet"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://cdn.jsdelivr.net/npm/lucide-static@0.483.0/font/lucide.css"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/drag-drop-touch-polyfill-es5-compiled@1.0.0/DragDropTouch.min.js"></script>
<link type="text/css" rel="stylesheet" href="style.css" />
</head>
<body>
<div class="toolbar">
<div class="toolbar-icons">
<div class="inline-images">
<i class="icon-puzzle" draggable="true" id="commodity"></i>
<i class="icon-files" draggable="true" id="documents"></i>
<i class="icon-cctv" draggable="true" id="fraudster"></i>
<i class="icon-banknote" draggable="true" id="money"></i>
<i class="icon-building" draggable="true" id="office"></i>
</div>
</div>
<div style="margin-left: auto">
<form>
<div class="switch switch--horizontal">
<input type="radio" name="drag-action" value="nodes" />
<label for="nodes">Move nodes</label>
<input
type="radio"
name="drag-action"
value="links"
checked="checked"
/>
<label for="links">Connect links</label>
<span class="toggle-outside">
<span class="toggle-inside"></span>
</span>
</div>
</form>
</div>
</div>
<div id="graph-container"></div>
<script src="index.js"></script>
</body>
</html>
css
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family: 'IBM Plex Sans', Arial, Helvetica, sans-serif;
}
#graph-container {
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 125px 0 0 0;
overflow: hidden;
position: absolute;
}
.toolbar {
display: flex;
top: 40px;
right: 20px;
padding: 10px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
border-radius: 4px;
background: #ffffff;
color: #222222;
height: 50px;
}
.toolbar-icons i {
font-size: 2.5em;
}
.toolbar .section {
position: relative;
display: block;
}
.instructions {
top: 0;
left: 0;
right: 0;
margin: 0;
border-bottom: 1px solid #aaa;
}
.instructions .message {
font-size: 16px;
margin: 8px 10px;
}
form {
float: right;
}
img {
width: 50px;
height: 50px;
}
.toolbar-icons {
flex-basis: 70%;
}
.inline-images {
display: flex;
justify-content: space-between;
}
.switch {
width: 100%;
position: relative;
}
.switch input {
position: absolute;
top: 0px;
z-index: 2;
opacity: 0;
cursor: pointer;
}
.switch input:checked {
z-index: 1;
}
.switch input:checked + label {
opacity: 1;
cursor: default;
}
.switch input:not(:checked) + label:hover {
opacity: 0.5;
}
.switch label {
color: #222222;
opacity: 0.33;
transition: opacity 0.25s ease;
cursor: pointer;
}
.switch .toggle-outside {
height: 100%;
border-radius: 2rem;
overflow: hidden;
transition: 0.25s ease all;
}
.switch .toggle-inside {
border-radius: 2.5rem;
background: #4a4a4a;
position: absolute;
transition: 0.25s ease all;
top: 0.25rem;
}
.switch--horizontal {
width: 18rem;
height: 2rem;
margin: 10px auto;
font-size: 0;
margin-bottom: 1rem;
}
.switch--horizontal input {
height: 2rem;
width: 5rem;
left: 5rem;
margin: 0;
}
.switch--horizontal label {
font-size: 0.9rem;
line-height: 2rem;
display: inline-block;
width: 6rem;
height: 100%;
margin: 0;
text-align: center;
}
.switch--horizontal label:last-of-type {
margin-left: 5rem;
}
.switch--horizontal .toggle-outside {
background: #dddddd;
position: absolute;
width: 5rem;
left: 5.7rem;
}
.switch--horizontal .toggle-inside {
height: 1.5rem;
width: 1.5rem;
}
.switch--horizontal input:checked ~ .toggle-outside .toggle-inside {
left: 0.25rem;
}
.switch--horizontal input ~ input:checked ~ .toggle-outside .toggle-inside {
left: 3.25rem;
}
.switch--horizontal input:disabled ~ .toggle-outside .toggle-inside {
background: #9a9a9a;
}
.switch--horizontal input:disabled ~ label {
color: #9a9a9a;
}
#money:hover {
color: #56b956;
}
#commodity:hover {
color: #bd8362;
}
#documents:hover {
color: #cacaca;
}
#fraudster:hover {
color: #f7102f;
}
#office:hover {
color: #c7c7c7;
}
.inline-images > * {
cursor: pointer;
}