轮圈徽章
当你想展示另一件事时,颜色、形状、大小和图标通常都会被提及。 badges 是它自己的一个通道:固定在节点边缘的小指示器,按节点返回,不花费任何上述编码。
这里,样式映射拥有节点的 type,并且徽章在顶部带有两个独立的事实 - 节点有多少个注释,以及是否有人对此提出异议。单击蓝色计数可查看徽章处理其自己的单击;将鼠标悬停在任何徽章上即可显示其工具提示。
徽章会自动从右上角顺时针放置到空闲的角落,因此您很少会命名为 position。四个适合一个普通节点,两个适合一个有孩子的节点,因为扩展可供性拥有东侧;任何过去的东西都会折叠成一个 +n 来命名它隐藏的东西。
js
// Colour, shape and icon are already spent on the node's *type*, so the two extra facts —
// how many notes it carries and whether anyone disputes it — go on badges instead.
const badges = (node) => {
const { notes, state } = node.getData()
const list = []
if (notes) {
list.push({
text: String(notes),
color: '#2563eb',
title: `${notes} notes`,
onClick: () => window.alert(`Open the ${notes} notes on ${node.getData().name}`)
})
}
if (state === 'disputed') {
list.push({ text: '!', color: '#dc2626', title: 'Disputed' })
}
if (state === 'confirmed') {
list.push({ text: '✓', color: '#16a34a', title: 'Confirmed' })
}
return list
}
const options = {
render: {
nodeTypeAccessor: (node) => node.getData()?.type,
nodeStyleMap: {
case: { color: '#7c3aed', size: 30 },
host: { color: '#0891b2', shape: 'square', size: 24 },
address: { color: '#f59e0b', size: 22 },
file: { color: '#64748b', shape: 'hexagon', size: 22 }
},
defaultNodeStyle: {
badges,
strokeColor: '#ffffff',
strokeWidth: 2,
textColor: '#334155',
text: (node) => node.getData()?.name,
textVerticalShift: -1.7
}
},
simulation: {
// Badges add to a node's footprint, so the nodes need more room between them
// than the defaults give — otherwise a rim badge lands on its neighbour's label.
d3ManyBodyStrength: -420,
d3LinkDistance: 105
}
}js
const data = {
nodes: [
{ id: 'incident', data: { name: 'Incident', type: 'case', notes: 12, state: 'disputed' } },
{ id: 'host', data: { name: 'web-01', type: 'host', notes: 3 } },
{ id: 'ip', data: { name: '198.51.100.7', type: 'address', notes: 1, state: 'confirmed' } },
{ id: 'hash', data: { name: 'a94f…c2', type: 'file', state: 'disputed' } },
{ id: 'domain', data: { name: 'evil.example', type: 'address' } }
],
edges: [
{ from: 'incident', to: 'host' },
{ from: 'incident', to: 'hash' },
{ from: 'incident', to: 'ip' },
{ from: 'host', to: 'ip' },
{ from: 'ip', to: 'domain' },
{ from: 'hash', to: 'host' }
]
}