mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
NodeGraph: Edge color and stroke-dasharray support (#83855)
* Adds color and stroke-dasharray support for node graph edges Adds support for providing color, highlighted color, and visual display of node graph edges as dashed lines via stroke-dasharray. * Updates node graph documentation * Updates documentation Adds default for `highlightedColor` * Update docs/sources/panels-visualizations/visualizations/node-graph/index.md Co-authored-by: Fabrizio <135109076+fabrizio-grafana@users.noreply.github.com> * Update packages/grafana-data/src/utils/nodeGraph.ts Co-authored-by: Fabrizio <135109076+fabrizio-grafana@users.noreply.github.com> * Update docs/sources/panels-visualizations/visualizations/node-graph/index.md Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com> * Removes highlightedColor; deprecates highlighted Per [request](https://github.com/grafana/grafana/pull/83855#issuecomment-1999810826), deprecates `highlighted` in code and documentation, and removes `highlightedColor` as an additional property. `highlighted` will continue to be supported in its original state (makes the edge red), but is superseded if `color` is provided. * Update types.ts Missed a file in my last commit. Removes `highlightedColor` and deprecates `highlighted`. * Add test scenario in test data source --------- Co-authored-by: Fabrizio <135109076+fabrizio-grafana@users.noreply.github.com> Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com> Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
This commit is contained in:
@@ -4719,9 +4719,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/grafana-testdata-datasource/nodeGraphUtils.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/grafana-testdata-datasource/runStreams.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
|
@@ -104,13 +104,21 @@ Required fields:
|
||||
|
||||
Optional fields:
|
||||
|
||||
| Field name | Type | Description |
|
||||
| ------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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` |
|
||||
| Field name | Type | Description |
|
||||
| --------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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` |
|
||||
| color | string | Sets the default color of the edge. It can be an acceptable HTML color string. Default: `#999` |
|
||||
| strokeDasharray | string | Sets the pattern of dashes and gaps used to render the edge. If unset, a solid line is used as edge. For more information and examples, refer to the [`stroke-dasharray` MDN documentation](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray). |
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
Starting with 10.5, `highlighted` is deprecated.
|
||||
It will be removed in a future release.
|
||||
Use `color` to indicate a highlighted edge state instead.
|
||||
{{< /admonition >}}
|
||||
|
||||
### Nodes data frame structure
|
||||
|
||||
|
@@ -15,7 +15,7 @@ export enum NodeGraphDataFrameFieldNames {
|
||||
// grafana/ui [nodes]
|
||||
icon = 'icon',
|
||||
// Defines a single color if string (hex or html named value) or color mode config can be used as threshold or
|
||||
// gradient. arc__ fields must not be defined if used [nodes]
|
||||
// gradient. arc__ fields must not be defined if used [nodes + edges]
|
||||
color = 'color',
|
||||
|
||||
// Id of the source node [required] [edges]
|
||||
@@ -32,6 +32,10 @@ export enum NodeGraphDataFrameFieldNames {
|
||||
// Thickness of the edge [edges]
|
||||
thickness = 'thickness',
|
||||
|
||||
// Whether the node or edge should be highlighted (e.g., shown in red) in the UI
|
||||
// Whether the node or edge should be highlighted (e.g., shown in red) in the UI [nodes + edges]
|
||||
// @deprecated -- for edges use color instead
|
||||
highlighted = 'highlighted',
|
||||
|
||||
// Defines the stroke dash array for the edge [edges]. See SVG strokeDasharray definition for syntax.
|
||||
strokeDasharray = 'strokedasharray',
|
||||
}
|
||||
|
@@ -75,7 +75,7 @@ export interface SimulationQuery {
|
||||
export interface NodesQuery {
|
||||
count?: number;
|
||||
seed?: number;
|
||||
type?: ('random' | 'response_small' | 'response_medium' | 'random edges');
|
||||
type?: ('random' | 'response_small' | 'response_medium' | 'random edges' | 'feature_showcase');
|
||||
}
|
||||
|
||||
export interface USAQuery {
|
||||
|
@@ -11,10 +11,11 @@ package dataquery
|
||||
|
||||
// Defines values for NodesQueryType.
|
||||
const (
|
||||
NodesQueryTypeRandom NodesQueryType = "random"
|
||||
NodesQueryTypeRandomEdges NodesQueryType = "random edges"
|
||||
NodesQueryTypeResponseMedium NodesQueryType = "response_medium"
|
||||
NodesQueryTypeResponseSmall NodesQueryType = "response_small"
|
||||
NodesQueryTypeFeatureShowcase NodesQueryType = "feature_showcase"
|
||||
NodesQueryTypeRandom NodesQueryType = "random"
|
||||
NodesQueryTypeRandomEdges NodesQueryType = "random edges"
|
||||
NodesQueryTypeResponseMedium NodesQueryType = "response_medium"
|
||||
NodesQueryTypeResponseSmall NodesQueryType = "response_small"
|
||||
)
|
||||
|
||||
// Defines values for StreamingQueryType.
|
||||
|
@@ -54,4 +54,10 @@ export function NodeGraphEditor({ query, onChange }: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
const options: Array<NodesQuery['type']> = ['random', 'response_small', 'response_medium', 'random edges'];
|
||||
const options: Array<NodesQuery['type']> = [
|
||||
'random',
|
||||
'response_small',
|
||||
'response_medium',
|
||||
'random edges',
|
||||
'feature_showcase',
|
||||
];
|
||||
|
@@ -83,7 +83,7 @@ composableKinds: DataQuery: {
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#NodesQuery: {
|
||||
type?: "random" | "response_small" | "response_medium" | "random edges"
|
||||
type?: "random" | "response_small" | "response_medium" | "random edges" | "feature_showcase"
|
||||
count?: int64
|
||||
seed?: int64
|
||||
} @cuetsy(kind="interface")
|
||||
|
@@ -73,7 +73,7 @@ export interface SimulationQuery {
|
||||
export interface NodesQuery {
|
||||
count?: number;
|
||||
seed?: number;
|
||||
type?: ('random' | 'response_small' | 'response_medium' | 'random edges');
|
||||
type?: ('random' | 'response_small' | 'response_medium' | 'random edges' | 'feature_showcase');
|
||||
}
|
||||
|
||||
export interface USAQuery {
|
||||
|
@@ -22,7 +22,7 @@ import { DataSourceWithBackend, getBackendSrv, getGrafanaLiveSrv, getTemplateSrv
|
||||
|
||||
import { Scenario, TestDataDataQuery, TestDataQueryType } from './dataquery.gen';
|
||||
import { queryMetricTree } from './metricTree';
|
||||
import { generateRandomEdges, generateRandomNodes, savedNodesResponse } from './nodeGraphUtils';
|
||||
import { generateRandomEdges, generateRandomNodes, generateShowcaseData, savedNodesResponse } from './nodeGraphUtils';
|
||||
import { runStream } from './runStreams';
|
||||
import { flameGraphData, flameGraphDataDiff } from './testData/flameGraphResponse';
|
||||
import { TestDataVariableSupport } from './variables';
|
||||
@@ -237,6 +237,9 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataDataQuery>
|
||||
const type = target.nodes?.type || 'random';
|
||||
let frames: DataFrame[];
|
||||
switch (type) {
|
||||
case 'feature_showcase':
|
||||
frames = generateShowcaseData();
|
||||
break;
|
||||
case 'random':
|
||||
frames = generateRandomNodes(target.nodes?.count, target.nodes?.seed);
|
||||
break;
|
||||
|
@@ -7,6 +7,7 @@ import {
|
||||
MutableDataFrame,
|
||||
NodeGraphDataFrameFieldNames,
|
||||
DataFrame,
|
||||
addRow,
|
||||
} from '@grafana/data';
|
||||
|
||||
import * as serviceMapResponseSmall from './testData/serviceMapResponse';
|
||||
@@ -56,7 +57,74 @@ export function generateRandomNodes(count = 10, seed?: number) {
|
||||
nodes[sourceIndex].edges.push(nodes[targetIndex].id);
|
||||
}
|
||||
|
||||
const nodeFields: Record<string, Omit<FieldDTO, 'name'> & { values: any[] }> = {
|
||||
const { nodesFields, nodesFrame, edgesFrame } = makeDataFrames();
|
||||
|
||||
const edgesSet = new Set();
|
||||
for (const node of nodes) {
|
||||
nodesFields.id.values.push(node.id);
|
||||
nodesFields.title.values.push(node.title);
|
||||
nodesFields[NodeGraphDataFrameFieldNames.subTitle].values.push(node.subTitle);
|
||||
nodesFields[NodeGraphDataFrameFieldNames.mainStat].values.push(node.stat1);
|
||||
nodesFields[NodeGraphDataFrameFieldNames.secondaryStat].values.push(node.stat2);
|
||||
nodesFields.arc__success.values.push(node.success);
|
||||
nodesFields.arc__errors.values.push(node.error);
|
||||
const rnd = Math.random();
|
||||
nodesFields[NodeGraphDataFrameFieldNames.icon].values.push(rnd > 0.9 ? 'database' : rnd < 0.1 ? 'cloud' : '');
|
||||
nodesFields[NodeGraphDataFrameFieldNames.nodeRadius].values.push(Math.max(rnd * 100, 30)); // ensure a minimum radius of 30 or icons will not fit well in the node
|
||||
nodesFields[NodeGraphDataFrameFieldNames.highlighted].values.push(Math.random() > 0.5);
|
||||
|
||||
for (const edge of node.edges) {
|
||||
const id = `${node.id}--${edge}`;
|
||||
// We can have duplicate edges when we added some more by random
|
||||
if (edgesSet.has(id)) {
|
||||
continue;
|
||||
}
|
||||
edgesSet.add(id);
|
||||
edgesFrame.fields[0].values.push(`${node.id}--${edge}`);
|
||||
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;
|
||||
|
||||
return [nodesFrame, edgesFrame];
|
||||
}
|
||||
|
||||
function makeRandomNode(index: number) {
|
||||
const success = Math.random();
|
||||
const error = 1 - success;
|
||||
return {
|
||||
id: `service:${index}`,
|
||||
title: `service:${index}`,
|
||||
subTitle: 'service',
|
||||
success,
|
||||
error,
|
||||
stat1: Math.random(),
|
||||
stat2: Math.random(),
|
||||
edges: [],
|
||||
highlighted: Math.random() > 0.5,
|
||||
};
|
||||
}
|
||||
|
||||
export function savedNodesResponse(size: 'small' | 'medium'): [DataFrame, DataFrame] {
|
||||
const response = size === 'small' ? serviceMapResponseSmall : serviceMapResponsMedium;
|
||||
return [new MutableDataFrame(response.nodes), new MutableDataFrame(response.edges)];
|
||||
}
|
||||
|
||||
// Generates node graph data but only returns the edges
|
||||
export function generateRandomEdges(count = 10, seed = 1) {
|
||||
return generateRandomNodes(count, seed)[1];
|
||||
}
|
||||
|
||||
function makeDataFrames(): {
|
||||
nodesFrame: DataFrame;
|
||||
edgesFrame: DataFrame;
|
||||
nodesFields: Record<string, Omit<FieldDTO, 'name'> & { values: unknown[] }>;
|
||||
} {
|
||||
const nodesFields: Record<string, Omit<FieldDTO, 'name'> & { values: unknown[] }> = {
|
||||
[NodeGraphDataFrameFieldNames.id]: {
|
||||
values: [],
|
||||
type: FieldType.string,
|
||||
@@ -114,12 +182,20 @@ export function generateRandomNodes(count = 10, seed?: number) {
|
||||
values: [],
|
||||
type: FieldType.boolean,
|
||||
},
|
||||
|
||||
[NodeGraphDataFrameFieldNames.detail + 'test_value']: {
|
||||
values: [],
|
||||
config: {
|
||||
displayName: 'Test value',
|
||||
},
|
||||
type: FieldType.number,
|
||||
},
|
||||
};
|
||||
|
||||
const nodeFrame = new MutableDataFrame({
|
||||
const nodesFrame = new MutableDataFrame({
|
||||
name: 'nodes',
|
||||
fields: Object.keys(nodeFields).map((key) => ({
|
||||
...nodeFields[key],
|
||||
fields: Object.keys(nodesFields).map((key) => ({
|
||||
...nodesFields[key],
|
||||
name: key,
|
||||
})),
|
||||
meta: { preferredVisualisationType: 'nodeGraph' },
|
||||
@@ -134,67 +210,106 @@ export function generateRandomNodes(count = 10, seed?: number) {
|
||||
{ name: NodeGraphDataFrameFieldNames.mainStat, values: [], type: FieldType.number, config: {} },
|
||||
{ name: NodeGraphDataFrameFieldNames.highlighted, values: [], type: FieldType.boolean, config: {} },
|
||||
{ name: NodeGraphDataFrameFieldNames.thickness, values: [], type: FieldType.number, config: {} },
|
||||
{ name: NodeGraphDataFrameFieldNames.color, values: [], type: FieldType.string, config: {} },
|
||||
{ name: NodeGraphDataFrameFieldNames.strokeDasharray, values: [], type: FieldType.string, config: {} },
|
||||
],
|
||||
meta: { preferredVisualisationType: 'nodeGraph' },
|
||||
length: 0,
|
||||
};
|
||||
|
||||
const edgesSet = new Set();
|
||||
for (const node of nodes) {
|
||||
nodeFields.id.values.push(node.id);
|
||||
nodeFields.title.values.push(node.title);
|
||||
nodeFields[NodeGraphDataFrameFieldNames.subTitle].values.push(node.subTitle);
|
||||
nodeFields[NodeGraphDataFrameFieldNames.mainStat].values.push(node.stat1);
|
||||
nodeFields[NodeGraphDataFrameFieldNames.secondaryStat].values.push(node.stat2);
|
||||
nodeFields.arc__success.values.push(node.success);
|
||||
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(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}`;
|
||||
// We can have duplicate edges when we added some more by random
|
||||
if (edgesSet.has(id)) {
|
||||
continue;
|
||||
}
|
||||
edgesSet.add(id);
|
||||
edgesFrame.fields[0].values.push(`${node.id}--${edge}`);
|
||||
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;
|
||||
|
||||
return [nodeFrame, edgesFrame];
|
||||
return { nodesFrame, edgesFrame, nodesFields };
|
||||
}
|
||||
|
||||
function makeRandomNode(index: number) {
|
||||
const success = Math.random();
|
||||
const error = 1 - success;
|
||||
return {
|
||||
id: `service:${index}`,
|
||||
title: `service:${index}`,
|
||||
subTitle: 'service',
|
||||
success,
|
||||
error,
|
||||
stat1: Math.random(),
|
||||
stat2: Math.random(),
|
||||
edges: [],
|
||||
highlighted: Math.random() > 0.5,
|
||||
};
|
||||
}
|
||||
export function generateShowcaseData() {
|
||||
const { nodesFrame, edgesFrame } = makeDataFrames();
|
||||
|
||||
export function savedNodesResponse(size: 'small' | 'medium'): [DataFrame, DataFrame] {
|
||||
const response = size === 'small' ? serviceMapResponseSmall : serviceMapResponsMedium;
|
||||
return [new MutableDataFrame(response.nodes), new MutableDataFrame(response.edges)];
|
||||
}
|
||||
addRow(nodesFrame, {
|
||||
[NodeGraphDataFrameFieldNames.id]: 'root',
|
||||
[NodeGraphDataFrameFieldNames.title]: 'root',
|
||||
[NodeGraphDataFrameFieldNames.subTitle]: 'client',
|
||||
[NodeGraphDataFrameFieldNames.mainStat]: 1234,
|
||||
[NodeGraphDataFrameFieldNames.secondaryStat]: 5678,
|
||||
arc__success: 0.5,
|
||||
arc__errors: 0.5,
|
||||
[NodeGraphDataFrameFieldNames.icon]: '',
|
||||
[NodeGraphDataFrameFieldNames.nodeRadius]: 40,
|
||||
[NodeGraphDataFrameFieldNames.highlighted]: false,
|
||||
[NodeGraphDataFrameFieldNames.detail + 'test_value']: 1,
|
||||
});
|
||||
|
||||
// Generates node graph data but only returns the edges
|
||||
export function generateRandomEdges(count = 10, seed = 1) {
|
||||
return generateRandomNodes(count, seed)[1];
|
||||
addRow(nodesFrame, {
|
||||
[NodeGraphDataFrameFieldNames.id]: 'app_service',
|
||||
[NodeGraphDataFrameFieldNames.title]: 'app service',
|
||||
[NodeGraphDataFrameFieldNames.subTitle]: 'with icon',
|
||||
[NodeGraphDataFrameFieldNames.mainStat]: 1.2,
|
||||
[NodeGraphDataFrameFieldNames.secondaryStat]: 2.3,
|
||||
arc__success: 1,
|
||||
arc__errors: 0,
|
||||
[NodeGraphDataFrameFieldNames.icon]: 'apps',
|
||||
[NodeGraphDataFrameFieldNames.nodeRadius]: 40,
|
||||
[NodeGraphDataFrameFieldNames.highlighted]: false,
|
||||
[NodeGraphDataFrameFieldNames.detail + 'test_value']: 42,
|
||||
});
|
||||
|
||||
addRow(edgesFrame, {
|
||||
[NodeGraphDataFrameFieldNames.id]: 'root-app_service',
|
||||
[NodeGraphDataFrameFieldNames.source]: 'root',
|
||||
[NodeGraphDataFrameFieldNames.target]: 'app_service',
|
||||
[NodeGraphDataFrameFieldNames.mainStat]: 3.4,
|
||||
[NodeGraphDataFrameFieldNames.secondaryStat]: 4.5,
|
||||
[NodeGraphDataFrameFieldNames.thickness]: 4,
|
||||
[NodeGraphDataFrameFieldNames.color]: '',
|
||||
[NodeGraphDataFrameFieldNames.strokeDasharray]: '',
|
||||
});
|
||||
|
||||
addRow(nodesFrame, {
|
||||
[NodeGraphDataFrameFieldNames.id]: 'auth_service',
|
||||
[NodeGraphDataFrameFieldNames.title]: 'auth service',
|
||||
[NodeGraphDataFrameFieldNames.subTitle]: 'highlighted',
|
||||
[NodeGraphDataFrameFieldNames.mainStat]: 3.4,
|
||||
[NodeGraphDataFrameFieldNames.secondaryStat]: 4.5,
|
||||
arc__success: 0,
|
||||
arc__errors: 1,
|
||||
[NodeGraphDataFrameFieldNames.icon]: '',
|
||||
[NodeGraphDataFrameFieldNames.nodeRadius]: 40,
|
||||
[NodeGraphDataFrameFieldNames.highlighted]: true,
|
||||
});
|
||||
|
||||
addRow(edgesFrame, {
|
||||
[NodeGraphDataFrameFieldNames.id]: 'root-auth_service',
|
||||
[NodeGraphDataFrameFieldNames.source]: 'root',
|
||||
[NodeGraphDataFrameFieldNames.target]: 'auth_service',
|
||||
[NodeGraphDataFrameFieldNames.mainStat]: 113.4,
|
||||
[NodeGraphDataFrameFieldNames.secondaryStat]: 4.511,
|
||||
[NodeGraphDataFrameFieldNames.thickness]: 8,
|
||||
[NodeGraphDataFrameFieldNames.color]: 'red',
|
||||
[NodeGraphDataFrameFieldNames.strokeDasharray]: '',
|
||||
});
|
||||
|
||||
addRow(nodesFrame, {
|
||||
[NodeGraphDataFrameFieldNames.id]: 'db',
|
||||
[NodeGraphDataFrameFieldNames.title]: 'db',
|
||||
[NodeGraphDataFrameFieldNames.subTitle]: 'bigger size',
|
||||
[NodeGraphDataFrameFieldNames.mainStat]: 9876.123,
|
||||
[NodeGraphDataFrameFieldNames.secondaryStat]: 123.9876,
|
||||
arc__success: 0.9,
|
||||
arc__errors: 0.1,
|
||||
[NodeGraphDataFrameFieldNames.icon]: 'database',
|
||||
[NodeGraphDataFrameFieldNames.nodeRadius]: 60,
|
||||
[NodeGraphDataFrameFieldNames.highlighted]: false,
|
||||
[NodeGraphDataFrameFieldNames.detail + 'test_value']: 1357,
|
||||
});
|
||||
|
||||
addRow(edgesFrame, {
|
||||
[NodeGraphDataFrameFieldNames.id]: 'auth_service-db',
|
||||
[NodeGraphDataFrameFieldNames.source]: 'auth_service',
|
||||
[NodeGraphDataFrameFieldNames.target]: 'db',
|
||||
[NodeGraphDataFrameFieldNames.mainStat]: 1139.4,
|
||||
[NodeGraphDataFrameFieldNames.secondaryStat]: 477.511,
|
||||
[NodeGraphDataFrameFieldNames.thickness]: 2,
|
||||
[NodeGraphDataFrameFieldNames.color]: 'blue',
|
||||
[NodeGraphDataFrameFieldNames.strokeDasharray]: '2 2',
|
||||
});
|
||||
|
||||
return [nodesFrame, edgesFrame];
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import { computeNodeCircumferenceStrokeWidth, nodeR } from './Node';
|
||||
import { EdgeDatum, NodeDatum } from './types';
|
||||
import { shortenLine } from './utils';
|
||||
|
||||
export const highlightedEdgeColor = '#a00';
|
||||
export const defaultHighlightedEdgeColor = '#a00';
|
||||
export const defaultEdgeColor = '#999';
|
||||
|
||||
interface Props {
|
||||
@@ -41,12 +41,18 @@ export const Edge = memo(function Edge(props: Props) {
|
||||
arrowHeadHeight
|
||||
);
|
||||
|
||||
const edgeColor = edge.color || defaultEdgeColor;
|
||||
|
||||
// @deprecated -- until 'highlighted' is removed we'll prioritize 'color'
|
||||
// in case both are provided
|
||||
const highlightedEdgeColor = edge.color || defaultHighlightedEdgeColor;
|
||||
|
||||
const markerId = `triangle-${edge.id}`;
|
||||
const coloredMarkerId = `triangle-colored-${edge.id}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<EdgeArrowMarker id={markerId} headHeight={arrowHeadHeight} />
|
||||
<EdgeArrowMarker id={markerId} fill={edgeColor} headHeight={arrowHeadHeight} />
|
||||
<EdgeArrowMarker id={coloredMarkerId} fill={highlightedEdgeColor} headHeight={arrowHeadHeight} />
|
||||
<g
|
||||
onClick={(event) => onClick(event, edge)}
|
||||
@@ -55,11 +61,12 @@ export const Edge = memo(function Edge(props: Props) {
|
||||
>
|
||||
<line
|
||||
strokeWidth={(hovering ? 1 : 0) + (edge.highlighted ? 1 : 0) + edge.thickness}
|
||||
stroke={edge.highlighted ? highlightedEdgeColor : defaultEdgeColor}
|
||||
stroke={edge.highlighted ? highlightedEdgeColor : edgeColor}
|
||||
x1={line.x1}
|
||||
y1={line.y1}
|
||||
x2={line.x2}
|
||||
y2={line.y2}
|
||||
strokeDasharray={edge.strokeDasharray}
|
||||
markerEnd={`url(#${edge.highlighted ? coloredMarkerId : markerId})`}
|
||||
/>
|
||||
<line
|
||||
|
@@ -35,8 +35,13 @@ export type EdgeDatum = LinkDatum & {
|
||||
dataFrameRowIndex: number;
|
||||
sourceNodeRadius: number;
|
||||
targetNodeRadius: number;
|
||||
/**
|
||||
* @deprecated -- for edges use color instead
|
||||
*/
|
||||
highlighted: boolean;
|
||||
thickness: number;
|
||||
color?: string;
|
||||
strokeDasharray?: string;
|
||||
};
|
||||
|
||||
// After layout is run D3 will change the string IDs for actual references to the nodes.
|
||||
|
@@ -86,8 +86,13 @@ export type EdgeFields = {
|
||||
mainStat?: Field;
|
||||
secondaryStat?: Field;
|
||||
details: Field[];
|
||||
/**
|
||||
* @deprecated use `color` instead
|
||||
*/
|
||||
highlighted?: Field;
|
||||
thickness?: Field;
|
||||
color?: Field;
|
||||
strokeDasharray?: Field;
|
||||
};
|
||||
|
||||
export function getEdgeFields(edges: DataFrame): EdgeFields {
|
||||
@@ -103,8 +108,11 @@ export function getEdgeFields(edges: DataFrame): EdgeFields {
|
||||
mainStat: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.mainStat.toLowerCase()),
|
||||
secondaryStat: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.secondaryStat.toLowerCase()),
|
||||
details: findFieldsByPrefix(edges, NodeGraphDataFrameFieldNames.detail.toLowerCase()),
|
||||
// @deprecated -- for edges use color instead
|
||||
highlighted: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.highlighted.toLowerCase()),
|
||||
thickness: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.thickness.toLowerCase()),
|
||||
color: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.color.toLowerCase()),
|
||||
strokeDasharray: fieldsCache.getFieldByName(NodeGraphDataFrameFieldNames.strokeDasharray.toLowerCase()),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -234,8 +242,11 @@ function processEdges(edges: DataFrame, edgeFields: EdgeFields, nodesMap: { [id:
|
||||
secondaryStat: edgeFields.secondaryStat
|
||||
? statToString(edgeFields.secondaryStat.config, edgeFields.secondaryStat.values[index])
|
||||
: '',
|
||||
// @deprecated -- for edges use color instead
|
||||
highlighted: edgeFields.highlighted?.values[index] || false,
|
||||
thickness: edgeFields.thickness?.values[index] || 1,
|
||||
color: edgeFields.color?.values[index],
|
||||
strokeDasharray: edgeFields.strokeDasharray?.values[index],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user