Appearance
Badge
This example shows how to add Badges to nodes. Nodes can have up to 4 badges (top left, top right, bottom left, bottom-right). Badges have a stroke, a background (color or image) and can contain text or icons. They can also be fixed in size or scale with the node.
ts
import { Ogma, Node } from '@linkurious/ogma';
const COLORS = [
'#965E04',
'#C89435',
'#F7A456',
'#AFCF8A',
'#7B39DE',
'#B095C0',
'#D24556',
'#93C2FA',
'#9DB09E',
'#F8C821'
];
const placeholder = document.createElement('span');
document.body.appendChild(placeholder);
placeholder.style.visibility = 'hidden';
// helper routine to get the icon HEX code
function getIconCode(className: string) {
placeholder.className = `icon-${className}`;
const code = getComputedStyle(placeholder, ':before').content;
return code[1];
}
const ICONS = [
getIconCode('mic'),
getIconCode('mic-off'),
getIconCode('shield-half'),
getIconCode('keyboard'),
getIconCode('circle-help'),
getIconCode('info'),
getIconCode('flag'),
getIconCode('settings')
];
const ROW_LENGTH = 5;
const ogma = new Ogma({
container: 'graph-container',
options: {
interactions: {
zoom: { minValue: () => 1e-5, maxValue: () => 1e7 }
}
}
});
const layer = ogma.layers.addCanvasLayer(ctx => {
const bounds = ogma.getNodes().getBoundingBox();
const rows = ogma.getNodes().size / ROW_LENGTH;
if (!isFinite(bounds.width) || bounds.width === 0) return;
ctx.beginPath();
const maxSize = bounds.maxScaledSize / 2;
const minX = bounds.minX - maxSize;
const minY = bounds.minY - maxSize * 2;
const height = bounds.height + maxSize * 2;
const stepY = height / rows + 20;
ctx.font = `${9}px IBM Plex Sans`;
ctx.fillStyle = ctx.strokeStyle = 'rgba(0, 0, 0, 0.75)';
ctx.lineWidth = 0.3;
//ctx.rect(minX, minY, width, height);
for (let i = 0; i < rows; i++) {
let x = minX - 5;
let y = minY + i * stepY;
ctx.fillText(i % 2 === 0 ? 'Fixed' : 'Scaled', x, y);
ctx.moveTo(x, y + 2);
ctx.lineTo(x + bounds.width + maxSize * 2, y + 2);
}
ctx.stroke();
});
layer.moveToBottom();
function getColor(node: Node) {
const id = +node.getId();
return COLORS[id % COLORS.length];
}
function isEvenRow(node: Node) {
return Math.floor(+node.getId() / ROW_LENGTH) % 2 === 0;
}
// Rules to specify the node style. Functions must be deterministic.
ogma.styles.addRule({
nodeAttributes: {
color: getColor,
text: {
content: n => 'Node ' + n.getId(),
font: 'IBM Plex Sans'
},
//radius: 15,
badges: {
bottomRight: {
color: '#fff',
scalingMethod: n => (isEvenRow(n) ? 'fixed' : 'scaled'),
text: {
font: 'Lucide', // Use FontAwesome icons.
color: '#336', // Use the node color.
// Retrieve the icon based on the node id.
content: n => ICONS[+n.getId() % ICONS.length]
},
minVisibleSize: 30,
stroke: {
color: getColor, // Use the node color
width: 2
},
scale: n => {
if (isEvenRow(n))
return ogma.styles.fontSizeToBadgeScale(
18,
+n.getAttribute('radius')
);
return 0.45;
}
}
}
},
nodeDependencies: {
self: { attributes: 'all' }
}
});
// Generate a random graph
const graph = await ogma.generate.random({ nodes: ROW_LENGTH * 2, edges: 0 });
graph.nodes.forEach((node, i) => {
node.attributes = { radius: 8 + (i % ROW_LENGTH) * 2 };
});
await ogma.setGraph(graph);
await ogma.layouts.grid({
locate: { padding: { top: 120 } },
colDistance: 20,
rowDistance: 50,
rows: 2
});
// Change the badge of a single node: use a
// background image and some text content.
ogma.getNode('0')!.setAttributes({
badges: {
bottomRight: {
image: 'flags/fr.svg',
text: {
font: 'IBM Plex Sans',
style: 'bold',
content: 'FR',
color: 'black'
}
}
}
});
html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link type="text/css" rel="stylesheet" href="styles.css" />
<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"
/>
</head>
<body>
<div id="graph-container"></div>
<script src="index.ts"></script>
</body>
</html>
css
#graph-container {
top: 0;
bottom: 0;
left: 0;
right: 0;
position: absolute;
margin: 0;
overflow: hidden;
}