mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Improvements to NodeGraph (#76879)
This commit is contained in:
parent
765defea1e
commit
b8d005ae23
@ -7367,7 +7367,8 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Styles should be written using objects.", "4"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "5"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "6"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "7"]
|
||||
[0, 0, 0, "Styles should be written using objects.", "7"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "8"]
|
||||
],
|
||||
"public/app/plugins/panel/nodeGraph/NodeGraph.tsx:5381": [
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"],
|
||||
|
@ -108,6 +108,8 @@ Optional fields:
|
||||
| mainstat | string/number | First stat shown in the overlay when hovering over the edge. It can be a string showing the value as is or it can be a number. If it is a number, any unit associated with that field is also shown |
|
||||
| secondarystat | string/number | Same as mainStat, but shown right under it. |
|
||||
| detail\_\_\* | string/number | Any field prefixed with `detail__` will be shown in the header of context menu when clicked on the edge. Use `config.displayName` for more human readable label. |
|
||||
| thickness | number | The thickness of the edge. Default: `1` |
|
||||
| highlighted | boolean | Sets whether the edge should be highlighted. Useful, for example, to represent a specific path in the graph by highlighting several nodes and edges. Default: `false` |
|
||||
|
||||
### Nodes data frame structure
|
||||
|
||||
@ -130,3 +132,4 @@ Optional fields:
|
||||
| color | string/number | Can be used to specify a single color instead of using the `arc__` fields to specify color sections. It can be either a string which should then be an acceptable HTML color string or it can be a number in which case the behaviour depends on `field.config.color.mode` setting. This can be for example used to create gradient colors controlled by the field value. |
|
||||
| icon | string | Name of the icon to show inside the node instead of the default stats. Only Grafana built in icons are allowed (see the available icons [here](https://developers.grafana.com/ui/latest/index.html?path=/story/docs-overview-icon--icons-overview)). |
|
||||
| nodeRadius | number | Radius value in pixels. Used to manage node size. |
|
||||
| highlighted | boolean | Sets whether the node should be highlighted. Useful for example to represent a specific path in the graph by highlighting several nodes and edges. Default: `false` |
|
||||
|
@ -26,5 +26,12 @@ export enum NodeGraphDataFrameFieldNames {
|
||||
// Prefix for fields which will be shown in a context menu [nodes + edges]
|
||||
detail = 'detail__',
|
||||
|
||||
// Radius of the node [nodes]
|
||||
nodeRadius = 'noderadius',
|
||||
|
||||
// Thickness of the edge [edges]
|
||||
thickness = 'thickness',
|
||||
|
||||
// Whether the node or edge should be highlighted (e.g., shown in red) in the UI
|
||||
highlighted = 'highlighted',
|
||||
}
|
||||
|
@ -105,6 +105,10 @@ export function generateRandomNodes(count = 10) {
|
||||
values: [],
|
||||
type: FieldType.number,
|
||||
},
|
||||
[NodeGraphDataFrameFieldNames.highlighted]: {
|
||||
values: [],
|
||||
type: FieldType.boolean,
|
||||
},
|
||||
};
|
||||
|
||||
const nodeFrame = new MutableDataFrame({
|
||||
@ -123,6 +127,8 @@ export function generateRandomNodes(count = 10) {
|
||||
{ name: NodeGraphDataFrameFieldNames.source, values: [], type: FieldType.string, config: {} },
|
||||
{ name: NodeGraphDataFrameFieldNames.target, values: [], type: FieldType.string, config: {} },
|
||||
{ name: NodeGraphDataFrameFieldNames.mainStat, values: [], type: FieldType.number, config: {} },
|
||||
{ name: NodeGraphDataFrameFieldNames.highlighted, values: [], type: FieldType.boolean, config: {} },
|
||||
{ name: NodeGraphDataFrameFieldNames.thickness, values: [], type: FieldType.number, config: {} },
|
||||
],
|
||||
meta: { preferredVisualisationType: 'nodeGraph' },
|
||||
length: 0,
|
||||
@ -139,7 +145,8 @@ export function generateRandomNodes(count = 10) {
|
||||
nodeFields.arc__errors.values.push(node.error);
|
||||
const rnd = Math.random();
|
||||
nodeFields[NodeGraphDataFrameFieldNames.icon].values.push(rnd > 0.9 ? 'database' : rnd < 0.1 ? 'cloud' : '');
|
||||
nodeFields[NodeGraphDataFrameFieldNames.nodeRadius].values.push(rnd > 0.5 ? 30 : 40);
|
||||
nodeFields[NodeGraphDataFrameFieldNames.nodeRadius].values.push(Math.max(rnd * 100, 30)); // ensure a minimum radius of 30 or icons will not fit well in the node
|
||||
nodeFields[NodeGraphDataFrameFieldNames.highlighted].values.push(Math.random() > 0.5);
|
||||
|
||||
for (const edge of node.edges) {
|
||||
const id = `${node.id}--${edge}`;
|
||||
@ -152,6 +159,8 @@ export function generateRandomNodes(count = 10) {
|
||||
edgesFrame.fields[1].values.push(node.id);
|
||||
edgesFrame.fields[2].values.push(edge);
|
||||
edgesFrame.fields[3].values.push(Math.random() * 100);
|
||||
edgesFrame.fields[4].values.push(Math.random() > 0.5);
|
||||
edgesFrame.fields[5].values.push(Math.ceil(Math.random() * 15));
|
||||
}
|
||||
}
|
||||
edgesFrame.length = edgesFrame.fields[0].values.length;
|
||||
@ -171,6 +180,7 @@ function makeRandomNode(index: number) {
|
||||
stat1: Math.random(),
|
||||
stat2: Math.random(),
|
||||
edges: [],
|
||||
highlighted: Math.random() > 0.5,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,13 @@
|
||||
import React, { MouseEvent, memo } from 'react';
|
||||
|
||||
import { nodeR } from './Node';
|
||||
import { EdgeArrowMarker } from './EdgeArrowMarker';
|
||||
import { computeNodeCircumferenceStrokeWidth, nodeR } from './Node';
|
||||
import { EdgeDatum, NodeDatum } from './types';
|
||||
import { shortenLine } from './utils';
|
||||
|
||||
export const highlightedEdgeColor = '#a00';
|
||||
export const defaultEdgeColor = '#999';
|
||||
|
||||
interface Props {
|
||||
edge: EdgeDatum;
|
||||
hovering: boolean;
|
||||
@ -11,6 +15,7 @@ interface Props {
|
||||
onMouseEnter: (id: string) => void;
|
||||
onMouseLeave: (id: string) => void;
|
||||
}
|
||||
|
||||
export const Edge = memo(function Edge(props: Props) {
|
||||
const { edge, onClick, onMouseEnter, onMouseLeave, hovering } = props;
|
||||
|
||||
@ -21,6 +26,7 @@ export const Edge = memo(function Edge(props: Props) {
|
||||
sourceNodeRadius: number;
|
||||
targetNodeRadius: number;
|
||||
};
|
||||
const arrowHeadHeight = 10 + edge.thickness * 2; // resized value, just to make the UI nicer
|
||||
|
||||
// As the nodes have some radius we want edges to end outside of the node circle.
|
||||
const line = shortenLine(
|
||||
@ -30,39 +36,47 @@ export const Edge = memo(function Edge(props: Props) {
|
||||
x2: target.x!,
|
||||
y2: target.y!,
|
||||
},
|
||||
sourceNodeRadius || nodeR,
|
||||
targetNodeRadius || nodeR
|
||||
sourceNodeRadius + computeNodeCircumferenceStrokeWidth(sourceNodeRadius) / 2 || nodeR,
|
||||
targetNodeRadius + computeNodeCircumferenceStrokeWidth(targetNodeRadius) / 2 || nodeR,
|
||||
arrowHeadHeight
|
||||
);
|
||||
|
||||
const markerId = `triangle-${edge.id}`;
|
||||
const coloredMarkerId = `triangle-colored-${edge.id}`;
|
||||
|
||||
return (
|
||||
<g
|
||||
onClick={(event) => onClick(event, edge)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
aria-label={`Edge from: ${source.id} to: ${target.id}`}
|
||||
>
|
||||
<line
|
||||
strokeWidth={hovering ? 2 : 1}
|
||||
stroke={'#999'}
|
||||
x1={line.x1}
|
||||
y1={line.y1}
|
||||
x2={line.x2}
|
||||
y2={line.y2}
|
||||
markerEnd="url(#triangle)"
|
||||
/>
|
||||
<line
|
||||
stroke={'transparent'}
|
||||
x1={line.x1}
|
||||
y1={line.y1}
|
||||
x2={line.x2}
|
||||
y2={line.y2}
|
||||
strokeWidth={20}
|
||||
onMouseEnter={() => {
|
||||
onMouseEnter(edge.id);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
onMouseLeave(edge.id);
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
<>
|
||||
<EdgeArrowMarker id={markerId} headHeight={arrowHeadHeight} />
|
||||
<EdgeArrowMarker id={coloredMarkerId} fill={highlightedEdgeColor} headHeight={arrowHeadHeight} />
|
||||
<g
|
||||
onClick={(event) => onClick(event, edge)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
aria-label={`Edge from: ${source.id} to: ${target.id}`}
|
||||
>
|
||||
<line
|
||||
strokeWidth={(hovering ? 1 : 0) + (edge.highlighted ? 1 : 0) + edge.thickness}
|
||||
stroke={edge.highlighted ? highlightedEdgeColor : defaultEdgeColor}
|
||||
x1={line.x1}
|
||||
y1={line.y1}
|
||||
x2={line.x2}
|
||||
y2={line.y2}
|
||||
markerEnd={`url(#${edge.highlighted ? coloredMarkerId : markerId})`}
|
||||
/>
|
||||
<line
|
||||
stroke={'transparent'}
|
||||
x1={line.x1}
|
||||
y1={line.y1}
|
||||
x2={line.x2}
|
||||
y2={line.y2}
|
||||
strokeWidth={20}
|
||||
onMouseEnter={() => {
|
||||
onMouseEnter(edge.id);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
onMouseLeave(edge.id);
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
@ -1,23 +1,33 @@
|
||||
import React from 'react';
|
||||
|
||||
import { defaultEdgeColor } from './Edge';
|
||||
|
||||
/**
|
||||
* In SVG you need to supply this kind of marker that can be then referenced from a line segment as an ending of the
|
||||
* line turning in into arrow. Needs to be included in the svg element and then referenced as markerEnd="url(#triangle)"
|
||||
*/
|
||||
export function EdgeArrowMarker() {
|
||||
export function EdgeArrowMarker({
|
||||
id = 'triangle',
|
||||
fill = defaultEdgeColor,
|
||||
headHeight = 10,
|
||||
}: {
|
||||
id?: string;
|
||||
fill?: string;
|
||||
headHeight?: number;
|
||||
}) {
|
||||
return (
|
||||
<defs>
|
||||
<marker
|
||||
id="triangle"
|
||||
id={id}
|
||||
viewBox="0 0 10 10"
|
||||
refX="8"
|
||||
refX="1" // shift the arrow head slightly closer to the center of the line it will be attached to, to ensure no empty space is shown between the line and the arrow head
|
||||
refY="5"
|
||||
markerUnits="userSpaceOnUse"
|
||||
markerWidth="10"
|
||||
markerHeight="10"
|
||||
markerWidth={headHeight} // equal to the height just for simplicily
|
||||
markerHeight={headHeight}
|
||||
orient="auto"
|
||||
>
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#999" />
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill={fill} />
|
||||
</marker>
|
||||
</defs>
|
||||
);
|
||||
|
@ -69,4 +69,5 @@ const nodeDatum = {
|
||||
mainStat: { name: 'stat', values: [1234], type: FieldType.number, config: {} },
|
||||
secondaryStat: { name: 'stat2', values: [9876], type: FieldType.number, config: {} },
|
||||
arcSections: [],
|
||||
highlighted: false,
|
||||
};
|
||||
|
@ -11,6 +11,7 @@ import { NodeDatum } from './types';
|
||||
import { statToString } from './utils';
|
||||
|
||||
export const nodeR = 40;
|
||||
export const highlightedNodeColor = '#a00';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, hovering: HoverState) => ({
|
||||
mainGroup: css`
|
||||
@ -24,6 +25,10 @@ const getStyles = (theme: GrafanaTheme2, hovering: HoverState) => ({
|
||||
fill: ${theme.components.panel.background};
|
||||
`,
|
||||
|
||||
filledCircle: css`
|
||||
fill: ${highlightedNodeColor};
|
||||
`,
|
||||
|
||||
hoverCircle: css`
|
||||
opacity: 0.5;
|
||||
fill: transparent;
|
||||
@ -66,6 +71,8 @@ const getStyles = (theme: GrafanaTheme2, hovering: HoverState) => ({
|
||||
`,
|
||||
});
|
||||
|
||||
export const computeNodeCircumferenceStrokeWidth = (nodeRadius: number) => Math.ceil(nodeRadius * 0.075);
|
||||
|
||||
export const Node = memo(function Node(props: {
|
||||
node: NodeDatum;
|
||||
hovering: HoverState;
|
||||
@ -78,6 +85,7 @@ export const Node = memo(function Node(props: {
|
||||
const styles = getStyles(theme, hovering);
|
||||
const isHovered = hovering === 'active';
|
||||
const nodeRadius = node.nodeRadius?.values[node.dataFrameRowIndex] || nodeR;
|
||||
const strokeWidth = computeNodeCircumferenceStrokeWidth(nodeRadius);
|
||||
|
||||
if (!(node.x !== undefined && node.y !== undefined)) {
|
||||
return null;
|
||||
@ -87,13 +95,13 @@ export const Node = memo(function Node(props: {
|
||||
<g data-node-id={node.id} className={styles.mainGroup} aria-label={`Node: ${node.title}`}>
|
||||
<circle
|
||||
data-testid={`node-circle-${node.id}`}
|
||||
className={styles.mainCircle}
|
||||
className={node.highlighted ? styles.filledCircle : styles.mainCircle}
|
||||
r={nodeRadius}
|
||||
cx={node.x}
|
||||
cy={node.y}
|
||||
/>
|
||||
{isHovered && (
|
||||
<circle className={styles.hoverCircle} r={nodeRadius - 3} cx={node.x} cy={node.y} strokeWidth={2} />
|
||||
<circle className={styles.hoverCircle} r={nodeRadius - 3} cx={node.x} cy={node.y} strokeWidth={strokeWidth} />
|
||||
)}
|
||||
<ColorCircle node={node} />
|
||||
<g className={styles.text} style={{ pointerEvents: 'none' }}>
|
||||
@ -172,14 +180,15 @@ function ColorCircle(props: { node: NodeDatum }) {
|
||||
const fullStat = node.arcSections.find((s) => s.values[node.dataFrameRowIndex] >= 1);
|
||||
const theme = useTheme2();
|
||||
const nodeRadius = node.nodeRadius?.values[node.dataFrameRowIndex] || nodeR;
|
||||
const strokeWidth = computeNodeCircumferenceStrokeWidth(nodeRadius);
|
||||
|
||||
if (fullStat) {
|
||||
// Doing arc with path does not work well so it's better to just do a circle in that case
|
||||
// Drawing a full circle with a `path` tag does not work well, it's better to use a `circle` tag in that case
|
||||
return (
|
||||
<circle
|
||||
fill="none"
|
||||
stroke={theme.visualization.getColorByName(fullStat.config.color?.fixedColor || '')}
|
||||
strokeWidth={2}
|
||||
strokeWidth={strokeWidth}
|
||||
r={nodeRadius}
|
||||
cx={node.x}
|
||||
cy={node.y}
|
||||
@ -194,7 +203,7 @@ function ColorCircle(props: { node: NodeDatum }) {
|
||||
<circle
|
||||
fill="none"
|
||||
stroke={node.color ? getColor(node.color, node.dataFrameRowIndex, theme) : 'gray'}
|
||||
strokeWidth={2}
|
||||
strokeWidth={strokeWidth}
|
||||
r={nodeRadius}
|
||||
cx={node.x}
|
||||
cy={node.y}
|
||||
@ -225,7 +234,7 @@ function ColorCircle(props: { node: NodeDatum }) {
|
||||
: value
|
||||
}
|
||||
color={theme.visualization.getColorByName(color)}
|
||||
strokeWidth={2}
|
||||
strokeWidth={strokeWidth}
|
||||
/>
|
||||
);
|
||||
acc.elements.push(el);
|
||||
|
@ -7,7 +7,6 @@ import { DataFrame, GrafanaTheme2, LinkModel } from '@grafana/data';
|
||||
import { Icon, Spinner, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { Edge } from './Edge';
|
||||
import { EdgeArrowMarker } from './EdgeArrowMarker';
|
||||
import { EdgeLabel } from './EdgeLabel';
|
||||
import { Legend } from './Legend';
|
||||
import { Marker } from './Marker';
|
||||
@ -208,7 +207,6 @@ export function NodeGraph({ getLinks, dataFrames, nodeLimit }: Props) {
|
||||
className={styles.mainGroup}
|
||||
style={{ transform: `scale(${scale}) translate(${Math.floor(position.x)}px, ${Math.floor(position.y)}px)` }}
|
||||
>
|
||||
<EdgeArrowMarker />
|
||||
{!config.gridLayout && (
|
||||
<Edges
|
||||
edges={edges}
|
||||
|
@ -74,6 +74,7 @@ function makeNode(index: number, incoming: number): NodeDatum {
|
||||
dataFrameRowIndex: 0,
|
||||
incoming,
|
||||
arcSections: [],
|
||||
highlighted: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -87,5 +88,7 @@ function makeEdge(source: number, target: number): EdgeDatum {
|
||||
dataFrameRowIndex: 0,
|
||||
sourceNodeRadius: 40,
|
||||
targetNodeRadius: 40,
|
||||
highlighted: false,
|
||||
thickness: 1,
|
||||
};
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ export type NodeDatum = SimulationNodeDatum & {
|
||||
color?: Field;
|
||||
icon?: IconName;
|
||||
nodeRadius?: Field;
|
||||
highlighted: boolean;
|
||||
};
|
||||
|
||||
export type NodeDatumFromEdge = NodeDatum & { mainStatNumeric?: number; secondaryStatNumeric?: number };
|
||||
@ -34,6 +35,8 @@ export type EdgeDatum = LinkDatum & {
|
||||
dataFrameRowIndex: number;
|
||||
sourceNodeRadius: number;
|
||||
targetNodeRadius: number;
|
||||
highlighted: boolean;
|
||||
thickness: number;
|
||||
};
|
||||
|
||||
// After layout is run D3 will change the string IDs for actual references to the nodes.
|
||||
|
@ -294,6 +294,7 @@ function makeNodeDatum(options: Partial<NodeDatum> = {}) {
|
||||
],
|
||||
color: colorField,
|
||||
dataFrameRowIndex: 0,
|
||||
highlighted: false,
|
||||
id: '0',
|
||||
incoming: 0,
|
||||
mainStat: {
|
||||
@ -334,6 +335,8 @@ function makeEdgeDatum(id: string, index: number, mainStat = '', secondaryStat =
|
||||
target: id.split('--')[1],
|
||||
sourceNodeRadius: 40,
|
||||
targetNodeRadius: 40,
|
||||
highlighted: false,
|
||||
thickness: 1,
|
||||
};
|
||||
}
|
||||
|
||||
@ -346,5 +349,6 @@ function makeNodeFromEdgeDatum(options: Partial<NodeDatum> = {}): NodeDatum {
|
||||
subTitle: '',
|
||||
title: 'service:0',
|
||||
...options,
|
||||
highlighted: false,
|
||||
};
|
||||
}
|
||||
|
@ -15,19 +15,32 @@ import { EdgeDatum, GraphFrame, NodeDatum, NodeDatumFromEdge, NodeGraphOptions }
|
||||
type Line = { x1: number; y1: number; x2: number; y2: number };
|
||||
|
||||
/**
|
||||
* Makes line shorter while keeping the middle in he same place.
|
||||
* Makes line shorter while keeping its middle in the same place.
|
||||
* This is manly used to add some empty space between an edge line and its source and target nodes, to make it nicer.
|
||||
*
|
||||
* @param line a line, where x1 and y1 are the coordinates of the source node center, and x2 and y2 are the coordinates of the target node center
|
||||
* @param sourceNodeRadius radius of the source node (possibly taking into account the thickness of the node circumference line, etc.)
|
||||
* @param targetNodeRadius radius of the target node (possibly taking into account the thickness of the node circumference line, etc.)
|
||||
* @param arrowHeadHeight height of the arrow head (in pixels)
|
||||
*/
|
||||
export function shortenLine(line: Line, sourceNodeRadius: number, targetNodeRadius: number): Line {
|
||||
export function shortenLine(line: Line, sourceNodeRadius: number, targetNodeRadius: number, arrowHeadHeight = 1): Line {
|
||||
const vx = line.x2 - line.x1;
|
||||
const vy = line.y2 - line.y1;
|
||||
const mag = Math.sqrt(vx * vx + vy * vy);
|
||||
const cosine = (line.x2 - line.x1) / mag;
|
||||
const sine = (line.y2 - line.y1) / mag;
|
||||
const scaledThickness = arrowHeadHeight - arrowHeadHeight / 10;
|
||||
|
||||
// Reduce the line length (along its main direction) by:
|
||||
// - the radius of the source node
|
||||
// - the radius of the target node,
|
||||
// - a constant value, just to add some empty space
|
||||
// - the height of the arrow head; the bigger the arrow head, the better is to add even more empty space
|
||||
return {
|
||||
x1: line.x1 + cosine * (sourceNodeRadius + 5),
|
||||
y1: line.y1 + sine * (sourceNodeRadius + 5),
|
||||
x2: line.x2 - cosine * (targetNodeRadius + 5),
|
||||
y2: line.y2 - sine * (targetNodeRadius + 5),
|
||||
x2: line.x2 - cosine * (targetNodeRadius + 3 + scaledThickness),
|
||||
y2: line.y2 - sine * (targetNodeRadius + 3 + scaledThickness),
|
||||
};
|
||||
}
|
||||
|
||||
@ -42,6 +55,7 @@ export type NodeFields = {
|
||||
color?: Field;
|
||||
icon?: Field;
|
||||
nodeRadius?: Field;
|
||||
highlighted?: Field;
|
||||
};
|
||||
|
||||
export function getNodeFields(nodes: DataFrame): NodeFields {
|
||||
@ -61,6 +75,7 @@ export function getNodeFields(nodes: DataFrame): NodeFields {
|
||||
color: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.color),
|
||||
icon: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.icon),
|
||||
nodeRadius: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.nodeRadius.toLowerCase()),
|
||||
highlighted: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.highlighted.toLowerCase()),
|
||||
};
|
||||
}
|
||||
|
||||
@ -71,6 +86,8 @@ export type EdgeFields = {
|
||||
mainStat?: Field;
|
||||
secondaryStat?: Field;
|
||||
details: Field[];
|
||||
highlighted?: Field;
|
||||
thickness?: Field;
|
||||
};
|
||||
|
||||
export function getEdgeFields(edges: DataFrame): EdgeFields {
|
||||
@ -86,6 +103,8 @@ export function getEdgeFields(edges: DataFrame): EdgeFields {
|
||||
mainStat: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.mainStat.toLowerCase()),
|
||||
secondaryStat: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.secondaryStat.toLowerCase()),
|
||||
details: findFieldsByPrefix(edges, NodeGraphDataFrameFieldNames.detail.toLowerCase()),
|
||||
highlighted: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.highlighted.toLowerCase()),
|
||||
thickness: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.thickness.toLowerCase()),
|
||||
};
|
||||
}
|
||||
|
||||
@ -215,6 +234,8 @@ function processEdges(edges: DataFrame, edgeFields: EdgeFields, nodesMap: { [id:
|
||||
secondaryStat: edgeFields.secondaryStat
|
||||
? statToString(edgeFields.secondaryStat.config, edgeFields.secondaryStat.values[index])
|
||||
: '',
|
||||
highlighted: edgeFields.highlighted?.values[index] || false,
|
||||
thickness: edgeFields.thickness?.values[index] || 1,
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -286,6 +307,7 @@ function makeSimpleNodeDatum(name: string, index: number): NodeDatumFromEdge {
|
||||
dataFrameRowIndex: index,
|
||||
incoming: 0,
|
||||
arcSections: [],
|
||||
highlighted: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -302,6 +324,7 @@ function makeNodeDatum(id: string, nodeFields: NodeFields, index: number): NodeD
|
||||
color: nodeFields.color,
|
||||
icon: nodeFields.icon?.values[index] || '',
|
||||
nodeRadius: nodeFields.nodeRadius,
|
||||
highlighted: nodeFields.highlighted?.values[index] || false,
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user