Skip to content

Commit

Permalink
remove a loop over the nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
asizemore committed May 8, 2024
1 parent dee9ffc commit 7b9a57e
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 60 deletions.
9 changes: 3 additions & 6 deletions packages/libs/components/src/plots/BipartiteNetworkPlot.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
BipartiteNetworkData,
NetworkPartition,
NodeData,
} from '../types/plots/network';
import { partition } from 'lodash';
import { BipartiteNetworkData, NetworkPartition } from '../types/plots/network';
import { LabelPosition } from './Node';
import { Ref, forwardRef, useMemo, SVGAttributes } from 'react';
import { gray } from '@veupathdb/coreui/lib/definitions/colors';
Expand Down Expand Up @@ -60,6 +55,7 @@ function BipartiteNetworkPlot(
partitions = EmptyBipartiteNetworkData.partitions,
containerStyles,
svgStyleOverrides,
getNodeMenuActions: getNodeActions,
} = props;

// Set up styles for the bipartite network and incorporate overrides
Expand Down Expand Up @@ -99,6 +95,7 @@ function BipartiteNetworkPlot(
labelPosition:
partitionIndex === 0 ? 'left' : ('right' as LabelPosition),
...node,
actions: getNodeActions?.(node.id),
};
}),
[
Expand Down
49 changes: 13 additions & 36 deletions packages/libs/components/src/plots/NetworkPlot.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LinkData, NodeData } from '../types/plots/network';
import { LinkData, NodeData, NodeMenuAction } from '../types/plots/network';
import { isNumber, orderBy } from 'lodash';
import { LabelPosition, NodeWithLabel } from './Node';
import { NodeWithLabel } from './Node';
import { Link } from './Link';
import { Graph } from '@visx/network';
import {
Expand All @@ -25,12 +25,6 @@ import { GlyphTriangle } from '@visx/visx';

import './NetworkPlot.css';

export interface NodeMenuAction {
label: ReactNode;
onClick?: () => void;
href?: string;
}

export interface NetworkPlotProps {
/** Network nodes */
nodes: NodeData[] | undefined;
Expand Down Expand Up @@ -59,10 +53,12 @@ export interface NetworkPlotProps {
const DEFAULT_PLOT_WIDTH = 500;
const DEFAULT_PLOT_HEIGHT = 500;

const emptyNodes: NodeData[] = [...Array(9).keys()].map((item) => ({
const emptyNodes: NodeData[] = [...Array(9).keys()].map((item, index) => ({
id: item.toString(),
color: gray[100],
stroke: gray[300],
x: 230 + 200 * Math.cos(2 * Math.PI * (index / 9)),
y: 230 + 200 * Math.sin(2 * Math.PI * (index / 9)),
}));
const emptyLinks: LinkData[] = [];

Expand All @@ -79,7 +75,6 @@ function NetworkPlot(props: NetworkPlotProps, ref: Ref<HTMLDivElement>) {
showSpinner = false,
labelTruncationLength = 20,
emptyNetworkContent,
getNodeMenuActions: getNodeActions,
annotations,
} = props;

Expand Down Expand Up @@ -119,20 +114,6 @@ function NetworkPlot(props: NetworkPlotProps, ref: Ref<HTMLDivElement>) {
...svgStyleOverrides,
};

// Node processing.
// Add actions and default coordinates. The default coordinates arrange nodes in a circle.
const processedNodes = useMemo(
() =>
nodes.map((node, index) => ({
labelPosition: 'right' as LabelPosition,
...node,
x: node.x ?? 230 + 200 * Math.cos(2 * Math.PI * (index / nodes.length)),
y: node.y ?? 230 + 200 * Math.sin(2 * Math.PI * (index / nodes.length)),
actions: getNodeActions?.(node.id),
})),
[getNodeActions, nodes]
);

// Link processing.
// Assign coordinates to links based on the newly created node coordinates.
// Additionally order links so that the highlighted ones get drawn on top (are at the end of the array).
Expand All @@ -141,12 +122,8 @@ function NetworkPlot(props: NetworkPlotProps, ref: Ref<HTMLDivElement>) {
// Put highlighted links on top of gray links.
orderBy(
links.map((link) => {
const sourceNode = processedNodes.find(
(node) => node.id === link.source.id
);
const targetNode = processedNodes.find(
(node) => node.id === link.target.id
);
const sourceNode = nodes.find((node) => node.id === link.source.id);
const targetNode = nodes.find((node) => node.id === link.target.id);
return {
...link,
source: {
Expand All @@ -173,10 +150,10 @@ function NetworkPlot(props: NetworkPlotProps, ref: Ref<HTMLDivElement>) {
// but that's okay, because the overlapping colors will be the same.
(link) => (link.color === '#eee' ? -1 : 1)
),
[links, highlightedNodeId, processedNodes]
[links, highlightedNodeId, nodes]
);

const activeNode = processedNodes.find((node) => node.id === activeNodeId);
const activeNode = nodes.find((node) => node.id === activeNodeId);

useEffect(() => {
const element = document.querySelector('.network-plot-container');
Expand All @@ -203,8 +180,8 @@ function NetworkPlot(props: NetworkPlotProps, ref: Ref<HTMLDivElement>) {
<div
style={{
position: 'absolute',
left: activeNode.x,
top: activeNode.y + 12,
left: activeNode.x && activeNode.x,
top: activeNode.y && activeNode.y + 12,
transform:
activeNode.labelPosition === 'left'
? `translate(calc(-2ch - ${
Expand Down Expand Up @@ -255,7 +232,7 @@ function NetworkPlot(props: NetworkPlotProps, ref: Ref<HTMLDivElement>) {
<svg {...svgStyles}>
<Graph
graph={{
nodes: processedNodes,
nodes: nodes,
links: processedLinks,
}}
// Using our Link component so that it uses our nice defaults and
Expand All @@ -276,7 +253,7 @@ function NetworkPlot(props: NetworkPlotProps, ref: Ref<HTMLDivElement>) {
node.labelPosition === 'left' ? rectX + 12 : rectWidth - 24;
return (
<>
{node.actions?.length && (
{node.actions && node.actions?.length && (
<g className="net-hover-dropdown">
<rect
rx="2.5"
Expand Down
31 changes: 13 additions & 18 deletions packages/libs/components/src/stories/plots/NetworkPlot.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Story, Meta } from '@storybook/react/types-6-0';
import { NodeData, LinkData, NetworkPlotData } from '../../types/plots/network';
import NetworkPlot, {
NetworkPlotProps,
import {
NodeData,
LinkData,
NetworkPlotData,
NodeMenuAction,
} from '../../plots/NetworkPlot';
} from '../../types/plots/network';
import NetworkPlot, { NetworkPlotProps } from '../../plots/NetworkPlot';
import { Text } from '@visx/text';
import { useEffect, useRef, useState } from 'react';

Expand Down Expand Up @@ -85,19 +87,6 @@ ManyPoints.args = {
...manyPointsData,
};

// Test the default layout
const defaultLayoutData = genNetwork(
50,
false,
false,
DEFAULT_PLOT_SIZE,
DEFAULT_PLOT_SIZE
);
export const DefaultLayout = Template.bind({});
DefaultLayout.args = {
...defaultLayoutData,
};

// A network with annotations.
// These can be used to add column labels in the bipartite network, call out
// a specific node of interest, or just generally add some more info.
Expand Down Expand Up @@ -170,9 +159,15 @@ function getNodeActions(nodeId: string): NodeMenuAction[] {
];
}

const simpleWithActions = simpleData;
simpleWithActions.nodes = simpleData.nodes.map((node) => ({
...node,
actions: getNodeActions(node.id),
}));

export const WithActions = Template.bind({});
WithActions.args = {
...simpleData,
...simpleWithActions,
getNodeMenuActions: getNodeActions,
};

Expand Down
10 changes: 10 additions & 0 deletions packages/libs/components/src/types/plots/network.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { LabelPosition } from '../../plots/Node';

// Types required for creating networks
import { ReactNode } from 'react';

export interface NodeMenuAction {
label: ReactNode;
onClick?: () => void;
href?: string;
}

export type NodeData = {
/** Node ID. Must be unique in the network! */
id: string;
Expand All @@ -20,6 +28,8 @@ export type NodeData = {
strokeWidth?: number;
/** Should the node label be drawn to the right or left of the node? */
labelPosition?: LabelPosition;
/** Action menu items for the node */
actions?: NodeMenuAction[];
};

export type LinkData = {
Expand Down

0 comments on commit 7b9a57e

Please sign in to comment.