Skip to content

Commit

Permalink
feat(network): add support for generic node datum
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Dec 31, 2021
1 parent 82a2d05 commit 375252a
Show file tree
Hide file tree
Showing 15 changed files with 371 additions and 241 deletions.
37 changes: 19 additions & 18 deletions packages/network/src/Network.tsx
@@ -1,20 +1,19 @@
import { Fragment, ReactNode, useCallback } from 'react'
import { Fragment, ReactNode, useCallback, createElement } from 'react'
import { Container, useDimensions, SvgWrapper, useTheme } from '@nivo/core'
import { useInheritedColor } from '@nivo/colors'
import { useTooltip } from '@nivo/tooltip'
import { svgDefaultProps } from './props'
import { svgDefaultProps } from './defaults'
import { useNetwork, useNodeColor, useLinkThickness } from './hooks'
import { NetworkNodes } from './NetworkNodes'
import { NetworkLinks } from './NetworkLinks'
import NetworkNodeTooltip from './NetworkNodeTooltip'
import { NetworkLayerId, NetworkSvgProps } from './types'
import { NetworkInputNode, NetworkLayerId, NetworkSvgProps } from './types'

type InnerNetworkProps = Omit<
NetworkSvgProps,
type InnerNetworkProps<N extends NetworkInputNode> = Omit<
NetworkSvgProps<N>,
'animate' | 'motionConfig' | 'renderWrapper' | 'theme'
>

const InnerNetwork = (props: InnerNetworkProps) => {
const InnerNetwork = <N extends NetworkInputNode>(props: InnerNetworkProps<N>) => {
const {
width,
height,
Expand All @@ -30,15 +29,16 @@ const InnerNetwork = (props: InnerNetworkProps) => {

layers = svgDefaultProps.layers,

nodeComponent = svgDefaultProps.nodeComponent,
nodeColor = svgDefaultProps.nodeColor,
nodeBorderWidth = svgDefaultProps.nodeBorderWidth,
nodeBorderColor = svgDefaultProps.nodeBorderColor,

linkThickness = svgDefaultProps.linkThickness,
linkColor = svgDefaultProps.linkColor,

tooltip = svgDefaultProps.tooltip,
isInteractive = svgDefaultProps.isInteractive,
nodeTooltip = svgDefaultProps.nodeTooltip,
onClick,

role = svgDefaultProps.role,
Expand All @@ -51,12 +51,12 @@ const InnerNetwork = (props: InnerNetworkProps) => {
)

const theme = useTheme()
const getColor = useNodeColor(nodeColor)
const getColor = useNodeColor<N>(nodeColor)
const getBorderColor = useInheritedColor(nodeBorderColor, theme)
const getLinkThickness = useLinkThickness(linkThickness)
const getLinkColor = useInheritedColor(linkColor, theme)

const [nodes, links] = useNetwork({
const [nodes, links] = useNetwork<N>({
nodes: rawNodes,
links: rawLinks,
linkDistance,
Expand All @@ -71,9 +71,9 @@ const InnerNetwork = (props: InnerNetworkProps) => {

const handleNodeHover = useCallback(
(node, event) => {
showTooltipFromEvent(<NetworkNodeTooltip node={node} tooltip={tooltip} />, event)
showTooltipFromEvent(createElement(nodeTooltip, { node }), event)
},
[showTooltipFromEvent, tooltip]
[showTooltipFromEvent, nodeTooltip]
)

const handleNodeLeave = useCallback(() => {
Expand All @@ -85,7 +85,7 @@ const InnerNetwork = (props: InnerNetworkProps) => {
nodes: null,
}

if (layers.includes('links')) {
if (layers.includes('links') && false) {
layerById.links = (
<NetworkLinks
key="links"
Expand All @@ -96,11 +96,12 @@ const InnerNetwork = (props: InnerNetworkProps) => {
)
}

if (layers.includes('crap')) {
if (layers.includes('nodes')) {
layerById.nodes = (
<NetworkNodes
<NetworkNodes<N>
key="nodes"
nodes={nodes}
nodeComponent={nodeComponent}
color={getColor}
borderWidth={nodeBorderWidth}
borderColor={getBorderColor}
Expand Down Expand Up @@ -135,14 +136,14 @@ const InnerNetwork = (props: InnerNetworkProps) => {
)
}

export const Network = ({
export const Network = <N extends NetworkInputNode = NetworkInputNode>({
isInteractive = svgDefaultProps.isInteractive,
animate = svgDefaultProps.animate,
motionConfig = svgDefaultProps.motionConfig,
theme,
renderWrapper,
...otherProps
}: NetworkSvgProps) => (
}: NetworkSvgProps<N>) => (
<Container
{...{
animate,
Expand All @@ -152,6 +153,6 @@ export const Network = ({
theme,
}}
>
<InnerNetwork isInteractive={isInteractive} {...otherProps} />
<InnerNetwork<N> isInteractive={isInteractive} {...otherProps} />
</Container>
)
21 changes: 12 additions & 9 deletions packages/network/src/NetworkCanvas.tsx
@@ -1,16 +1,19 @@
import { useCallback, useRef, useEffect, ForwardedRef } from 'react'
import { useCallback, useRef, useEffect } from 'react'
import * as React from 'react'
import { getDistance, getRelativeCursor, Container, useDimensions, useTheme } from '@nivo/core'
import { useInheritedColor } from '@nivo/colors'
import { useTooltip } from '@nivo/tooltip'
import { canvasDefaultProps } from './props'
import { canvasDefaultProps } from './defaults'
import { useNetwork, useNodeColor, useLinkThickness } from './hooks'
import NetworkNodeTooltip from './NetworkNodeTooltip'
import { NetworkCanvasProps } from './types'
import { NetworkNodeTooltip } from './NetworkNodeTooltip'
import { NetworkCanvasProps, NetworkInputNode } from './types'

type InnerNetworkCanvasProps = Omit<NetworkCanvasProps, 'renderWrapper' | 'theme'>
type InnerNetworkCanvasProps<N extends NetworkInputNode> = Omit<
NetworkCanvasProps<N>,
'renderWrapper' | 'theme'
>

const InnerNetworkCanvas = (props: InnerNetworkCanvasProps) => {
const InnerNetworkCanvas = <N extends NetworkInputNode>(props: InnerNetworkCanvasProps<N>) => {
const {
width,
height,
Expand Down Expand Up @@ -188,15 +191,15 @@ const InnerNetworkCanvas = (props: InnerNetworkCanvasProps) => {
)
}

export const NetworkCanvas = ({
export const NetworkCanvas = <N extends NetworkInputNode = NetworkInputNode>({
theme,
isInteractive = canvasDefaultProps.isInteractive,
animate = canvasDefaultProps.animate,
motionConfig = canvasDefaultProps.motionConfig,
renderWrapper,
...otherProps
}: NetworkCanvasProps) => (
}: NetworkCanvasProps<N>) => (
<Container {...{ isInteractive, animate, motionConfig, theme, renderWrapper }}>
<InnerNetworkCanvas isInteractive={isInteractive} {...otherProps} />
<InnerNetworkCanvas<N> isInteractive={isInteractive} {...otherProps} />
</Container>
)
34 changes: 17 additions & 17 deletions packages/network/src/NetworkLinks.tsx
@@ -1,4 +1,4 @@
import { createElement } from 'react'
import { createElement, useCallback, useMemo } from 'react'
import { useTransition } from '@react-spring/web'
import { useMotionConfig } from '@nivo/core'
import { NetworkLink } from './NetworkLink'
Expand All @@ -10,11 +10,24 @@ interface NetworkLinksProps {
linkColor: (link: ComputedLink) => string
}

const getRegularTransition = (linkColor: NetworkLinksProps['linkColor']) => (
link: ComputedLink
) => ({
x1: link.source.x,
y1: link.source.y,
x2: link.target.x,
y2: link.target.y,
color: linkColor(link),
opacity: 1,
})

export const NetworkLinks = ({ links, linkThickness, linkColor }: NetworkLinksProps) => {
const { animate, config: springConfig } = useMotionConfig()

console.log('immediate', !animate)

const regularTransition = useMemo(() => getRegularTransition(linkColor), [linkColor])

const transition = useTransition<
ComputedLink,
{
Expand All @@ -27,6 +40,7 @@ export const NetworkLinks = ({ links, linkThickness, linkColor }: NetworkLinksPr
}
>(links, {
keys: link => link.id,
initial: regularTransition,
from: link => ({
x1: link.source.x,
y1: link.source.y,
Expand All @@ -35,22 +49,8 @@ export const NetworkLinks = ({ links, linkThickness, linkColor }: NetworkLinksPr
color: linkColor(link),
opacity: 0,
}),
enter: link => ({
x1: link.source.x,
y1: link.source.y,
x2: link.target.x,
y2: link.target.y,
color: linkColor(link),
opacity: 1,
}),
update: link => ({
x1: link.source.x,
y1: link.source.y,
x2: link.target.x,
y2: link.target.y,
color: linkColor(link),
opacity: 1,
}),
enter: regularTransition,
update: regularTransition,
leave: link => ({
x1: link.source.x,
y1: link.source.y,
Expand Down
19 changes: 4 additions & 15 deletions packages/network/src/NetworkNode.tsx
@@ -1,25 +1,14 @@
import { MouseEvent } from 'react'
import { AnimatedProps, animated, to } from '@react-spring/web'
import { ComputedNode, NodeAnimatedProps } from './types'
import { animated, to } from '@react-spring/web'
import { NetworkInputNode, NetworkNodeProps } from './types'

interface NetworkNodeProps {
node: ComputedNode
animated: AnimatedProps<NodeAnimatedProps>
scale?: number
onClick?: (node: ComputedNode, event: MouseEvent) => void
onMouseEnter?: (node: ComputedNode, event: MouseEvent) => void
onMouseMove?: (node: ComputedNode, event: MouseEvent) => void
onMouseLeave?: (node: ComputedNode, event: MouseEvent) => void
}

export const NetworkNode = ({
export const NetworkNode = <N extends NetworkInputNode>({
node,
animated: animatedProps,
onClick,
onMouseEnter,
onMouseMove,
onMouseLeave,
}: NetworkNodeProps) => {
}: NetworkNodeProps<N>) => {
return (
<animated.circle
transform={to(
Expand Down
5 changes: 1 addition & 4 deletions packages/network/src/NetworkNodeTooltip.tsx
@@ -1,14 +1,11 @@
import { memo } from 'react'
import { BasicTooltip } from '@nivo/tooltip'
import { NetworkNodeTooltipProps } from './types'

const NetworkNodeTooltip = ({ node }: NetworkNodeTooltipProps) => (
export const NetworkNodeTooltip = ({ node }: NetworkNodeTooltipProps) => (
<BasicTooltip
id={node.id}
enableChip={true}
color={node.color}
// renderContent={typeof tooltip === 'function' ? tooltip.bind(null, { ...node }) : null}
/>
)

export default memo(NetworkNodeTooltip)

0 comments on commit 375252a

Please sign in to comment.