Skip to content

Commit c3d5dd1

Browse files
committedDec 31, 2021
feat(network): types are now valid
1 parent 375252a commit c3d5dd1

File tree

11 files changed

+243
-163
lines changed

11 files changed

+243
-163
lines changed
 

‎packages/network/src/Network.tsx

+32-34
Original file line numberDiff line numberDiff line change
@@ -13,37 +13,35 @@ type InnerNetworkProps<N extends NetworkInputNode> = Omit<
1313
'animate' | 'motionConfig' | 'renderWrapper' | 'theme'
1414
>
1515

16-
const InnerNetwork = <N extends NetworkInputNode>(props: InnerNetworkProps<N>) => {
17-
const {
18-
width,
19-
height,
20-
margin: partialMargin,
16+
const InnerNetwork = <N extends NetworkInputNode>({
17+
width,
18+
height,
19+
margin: partialMargin,
2120

22-
data: { nodes: rawNodes, links: rawLinks },
21+
data: { nodes: rawNodes, links: rawLinks },
2322

24-
linkDistance = svgDefaultProps.linkDistance,
25-
repulsivity = svgDefaultProps.repulsivity,
26-
distanceMin = svgDefaultProps.distanceMin,
27-
distanceMax = svgDefaultProps.distanceMax,
28-
iterations = svgDefaultProps.iterations,
23+
linkDistance = svgDefaultProps.linkDistance,
24+
repulsivity = svgDefaultProps.repulsivity,
25+
distanceMin = svgDefaultProps.distanceMin,
26+
distanceMax = svgDefaultProps.distanceMax,
27+
iterations = svgDefaultProps.iterations,
2928

30-
layers = svgDefaultProps.layers,
29+
layers = svgDefaultProps.layers,
3130

32-
nodeComponent = svgDefaultProps.nodeComponent,
33-
nodeColor = svgDefaultProps.nodeColor,
34-
nodeBorderWidth = svgDefaultProps.nodeBorderWidth,
35-
nodeBorderColor = svgDefaultProps.nodeBorderColor,
31+
nodeComponent = svgDefaultProps.nodeComponent,
32+
nodeColor = svgDefaultProps.nodeColor,
33+
nodeBorderWidth = svgDefaultProps.nodeBorderWidth,
34+
nodeBorderColor = svgDefaultProps.nodeBorderColor,
3635

37-
linkThickness = svgDefaultProps.linkThickness,
38-
linkColor = svgDefaultProps.linkColor,
36+
linkThickness = svgDefaultProps.linkThickness,
37+
linkColor = svgDefaultProps.linkColor,
3938

40-
isInteractive = svgDefaultProps.isInteractive,
41-
nodeTooltip = svgDefaultProps.nodeTooltip,
42-
onClick,
43-
44-
role = svgDefaultProps.role,
45-
} = props
39+
isInteractive = svgDefaultProps.isInteractive,
40+
nodeTooltip = svgDefaultProps.nodeTooltip,
41+
onClick,
4642

43+
role = svgDefaultProps.role,
44+
}: InnerNetworkProps<N>) => {
4745
const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions(
4846
width,
4947
height,
@@ -85,9 +83,9 @@ const InnerNetwork = <N extends NetworkInputNode>(props: InnerNetworkProps<N>) =
8583
nodes: null,
8684
}
8785

88-
if (layers.includes('links') && false) {
86+
if (layers.includes('links') && links !== null) {
8987
layerById.links = (
90-
<NetworkLinks
88+
<NetworkLinks<N>
9189
key="links"
9290
links={links}
9391
linkThickness={getLinkThickness}
@@ -96,7 +94,7 @@ const InnerNetwork = <N extends NetworkInputNode>(props: InnerNetworkProps<N>) =
9694
)
9795
}
9896

99-
if (layers.includes('nodes')) {
97+
if (layers.includes('nodes') && nodes !== null) {
10098
layerById.nodes = (
10199
<NetworkNodes<N>
102100
key="nodes"
@@ -119,18 +117,18 @@ const InnerNetwork = <N extends NetworkInputNode>(props: InnerNetworkProps<N>) =
119117
if (typeof layer === 'function') {
120118
return (
121119
<Fragment key={i}>
122-
{layer({
123-
...props,
124-
innerWidth,
125-
innerHeight,
126-
nodes,
127-
links,
120+
{createElement(layer, {
121+
// ...props,
122+
// innerWidth,
123+
// innerHeight,
124+
nodes: nodes || [],
125+
links: links || [],
128126
})}
129127
</Fragment>
130128
)
131129
}
132130

133-
return layerById[layer]
131+
return layerById?.[layer] ?? null
134132
})}
135133
</SvgWrapper>
136134
)

‎packages/network/src/NetworkCanvas.tsx

+40-39
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,51 @@
1-
import { useCallback, useRef, useEffect } from 'react'
2-
import * as React from 'react'
1+
import { useCallback, useRef, useEffect, createElement, MouseEvent } from 'react'
32
import { getDistance, getRelativeCursor, Container, useDimensions, useTheme } from '@nivo/core'
43
import { useInheritedColor } from '@nivo/colors'
54
import { useTooltip } from '@nivo/tooltip'
65
import { canvasDefaultProps } from './defaults'
76
import { useNetwork, useNodeColor, useLinkThickness } from './hooks'
8-
import { NetworkNodeTooltip } from './NetworkNodeTooltip'
97
import { NetworkCanvasProps, NetworkInputNode } from './types'
108

119
type InnerNetworkCanvasProps<N extends NetworkInputNode> = Omit<
1210
NetworkCanvasProps<N>,
1311
'renderWrapper' | 'theme'
1412
>
1513

16-
const InnerNetworkCanvas = <N extends NetworkInputNode>(props: InnerNetworkCanvasProps<N>) => {
17-
const {
18-
width,
19-
height,
20-
margin: partialMargin,
21-
pixelRatio = canvasDefaultProps.pixelRatio,
22-
23-
data: { nodes: rawNodes, links: rawLinks },
14+
const InnerNetworkCanvas = <N extends NetworkInputNode>({
15+
width,
16+
height,
17+
margin: partialMargin,
18+
pixelRatio = canvasDefaultProps.pixelRatio,
2419

25-
linkDistance = canvasDefaultProps.linkDistance,
26-
repulsivity = canvasDefaultProps.repulsivity,
27-
distanceMin = canvasDefaultProps.distanceMin,
28-
distanceMax = canvasDefaultProps.distanceMax,
29-
iterations = canvasDefaultProps.iterations,
20+
data: { nodes: rawNodes, links: rawLinks },
3021

31-
layers = canvasDefaultProps.layers,
22+
linkDistance = canvasDefaultProps.linkDistance,
23+
repulsivity = canvasDefaultProps.repulsivity,
24+
distanceMin = canvasDefaultProps.distanceMin,
25+
distanceMax = canvasDefaultProps.distanceMax,
26+
iterations = canvasDefaultProps.iterations,
3227

33-
nodeColor = canvasDefaultProps.nodeColor,
34-
nodeBorderWidth = canvasDefaultProps.nodeBorderWidth,
35-
nodeBorderColor = canvasDefaultProps.nodeBorderColor,
28+
layers = canvasDefaultProps.layers,
3629

37-
linkThickness = canvasDefaultProps.linkThickness,
38-
linkColor = canvasDefaultProps.linkColor,
30+
nodeColor = canvasDefaultProps.nodeColor,
31+
nodeBorderWidth = canvasDefaultProps.nodeBorderWidth,
32+
nodeBorderColor = canvasDefaultProps.nodeBorderColor,
3933

40-
isInteractive = canvasDefaultProps.isInteractive,
41-
tooltip = canvasDefaultProps.tooltip,
42-
onClick,
43-
} = props
34+
linkThickness = canvasDefaultProps.linkThickness,
35+
linkColor = canvasDefaultProps.linkColor,
4436

45-
const canvasEl = useRef(null)
37+
isInteractive = canvasDefaultProps.isInteractive,
38+
nodeTooltip = canvasDefaultProps.nodeTooltip,
39+
onClick,
40+
}: InnerNetworkCanvasProps<N>) => {
41+
const canvasEl = useRef<HTMLCanvasElement | null>(null)
4642
const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions(
4743
width,
4844
height,
4945
partialMargin
5046
)
5147

52-
const [nodes, links] = useNetwork({
48+
const [nodes, links] = useNetwork<N>({
5349
nodes: rawNodes,
5450
links: rawLinks,
5551
linkDistance,
@@ -67,10 +63,12 @@ const InnerNetworkCanvas = <N extends NetworkInputNode>(props: InnerNetworkCanva
6763
const getLinkColor = useInheritedColor(linkColor, theme)
6864

6965
useEffect(() => {
66+
if (canvasEl.current === null) return
67+
7068
canvasEl.current.width = outerWidth * pixelRatio
7169
canvasEl.current.height = outerHeight * pixelRatio
7270

73-
const ctx = canvasEl.current.getContext('2d')
71+
const ctx = canvasEl.current.getContext('2d')!
7472

7573
ctx.scale(pixelRatio, pixelRatio)
7674

@@ -79,7 +77,7 @@ const InnerNetworkCanvas = <N extends NetworkInputNode>(props: InnerNetworkCanva
7977
ctx.translate(margin.left, margin.top)
8078

8179
layers.forEach(layer => {
82-
if (layer === 'links') {
80+
if (layer === 'links' && links !== null) {
8381
links.forEach(link => {
8482
ctx.strokeStyle = getLinkColor(link)
8583
ctx.lineWidth = getLinkThickness(link)
@@ -88,7 +86,7 @@ const InnerNetworkCanvas = <N extends NetworkInputNode>(props: InnerNetworkCanva
8886
ctx.lineTo(link.target.x, link.target.y)
8987
ctx.stroke()
9088
})
91-
} else if (layer === 'nodes') {
89+
} else if (layer === 'nodes' && nodes !== null) {
9290
nodes.forEach(node => {
9391
ctx.fillStyle = getNodeColor(node)
9492
ctx.beginPath()
@@ -101,9 +99,9 @@ const InnerNetworkCanvas = <N extends NetworkInputNode>(props: InnerNetworkCanva
10199
ctx.stroke()
102100
}
103101
})
104-
} else if (typeof layer === 'function') {
102+
} else if (typeof layer === 'function' && nodes !== null && links !== null) {
105103
layer(ctx, {
106-
...props,
104+
// ...props,
107105
nodes,
108106
links,
109107
})
@@ -113,6 +111,9 @@ const InnerNetworkCanvas = <N extends NetworkInputNode>(props: InnerNetworkCanva
113111
canvasEl,
114112
outerWidth,
115113
outerHeight,
114+
margin.left,
115+
margin.top,
116+
pixelRatio,
116117
layers,
117118
theme,
118119
nodes,
@@ -125,8 +126,8 @@ const InnerNetworkCanvas = <N extends NetworkInputNode>(props: InnerNetworkCanva
125126
])
126127

127128
const getNodeFromMouseEvent = useCallback(
128-
event => {
129-
if (!canvasEl.current) return null
129+
(event: MouseEvent) => {
130+
if (!canvasEl.current || nodes === null) return undefined
130131

131132
const [x, y] = getRelativeCursor(canvasEl.current, event)
132133

@@ -146,23 +147,23 @@ const InnerNetworkCanvas = <N extends NetworkInputNode>(props: InnerNetworkCanva
146147
const { showTooltipFromEvent, hideTooltip } = useTooltip()
147148

148149
const handleMouseHover = useCallback(
149-
event => {
150+
(event: MouseEvent) => {
150151
const node = getNodeFromMouseEvent(event)
151152
if (node) {
152-
showTooltipFromEvent(<NetworkNodeTooltip node={node} tooltip={tooltip} />, event)
153+
showTooltipFromEvent(createElement(nodeTooltip, { node }), event)
153154
} else {
154155
hideTooltip()
155156
}
156157
},
157-
[getNodeFromMouseEvent, showTooltipFromEvent, tooltip, hideTooltip]
158+
[getNodeFromMouseEvent, showTooltipFromEvent, nodeTooltip, hideTooltip]
158159
)
159160

160161
const handleMouseLeave = useCallback(() => {
161162
hideTooltip()
162163
}, [hideTooltip])
163164

164165
const handleClick = useCallback(
165-
event => {
166+
(event: MouseEvent) => {
166167
if (!onClick) return
167168

168169
const node = getNodeFromMouseEvent(event)

‎packages/network/src/NetworkLink.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { AnimatedProps, animated } from '@react-spring/web'
2-
import { ComputedLink } from './types'
2+
import { ComputedLink, NetworkInputNode } from './types'
33

4-
interface NetworkLinkProps {
5-
link: ComputedLink
4+
interface NetworkLinkProps<N extends NetworkInputNode> {
5+
link: ComputedLink<N>
66
thickness: number
77
animated: AnimatedProps<{
88
x1: number
@@ -13,7 +13,10 @@ interface NetworkLinkProps {
1313
}>
1414
}
1515

16-
export const NetworkLink = ({ thickness, animated: animatedProps }: NetworkLinkProps) => {
16+
export const NetworkLink = <N extends NetworkInputNode>({
17+
thickness,
18+
animated: animatedProps,
19+
}: NetworkLinkProps<N>) => {
1720
return (
1821
<animated.line
1922
stroke={animatedProps.color}

‎packages/network/src/NetworkLinks.tsx

+47-30
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,29 @@
1-
import { createElement, useCallback, useMemo } from 'react'
1+
import { createElement, useMemo } from 'react'
22
import { useTransition } from '@react-spring/web'
33
import { useMotionConfig } from '@nivo/core'
44
import { NetworkLink } from './NetworkLink'
5-
import { ComputedLink } from './types'
5+
import { ComputedLink, NetworkInputNode } from './types'
66

7-
interface NetworkLinksProps {
8-
links: ComputedLink[]
9-
linkThickness: (link: ComputedLink) => number
10-
linkColor: (link: ComputedLink) => string
7+
interface NetworkLinksProps<N extends NetworkInputNode> {
8+
links: ComputedLink<N>[]
9+
linkThickness: (link: ComputedLink<N>) => number
10+
linkColor: (link: ComputedLink<N>) => string
1111
}
1212

13-
const getRegularTransition = (linkColor: NetworkLinksProps['linkColor']) => (
14-
link: ComputedLink
15-
) => ({
13+
const getEnterTransition = <N extends NetworkInputNode>(
14+
linkColor: NetworkLinksProps<N>['linkColor']
15+
) => (link: ComputedLink<N>) => ({
16+
x1: link.source.x,
17+
y1: link.source.y,
18+
x2: link.source.x,
19+
y2: link.source.y,
20+
color: linkColor(link),
21+
opacity: 0,
22+
})
23+
24+
const getRegularTransition = <N extends NetworkInputNode>(
25+
linkColor: NetworkLinksProps<N>['linkColor']
26+
) => (link: ComputedLink<N>) => ({
1627
x1: link.source.x,
1728
y1: link.source.y,
1829
x2: link.target.x,
@@ -21,15 +32,35 @@ const getRegularTransition = (linkColor: NetworkLinksProps['linkColor']) => (
2132
opacity: 1,
2233
})
2334

24-
export const NetworkLinks = ({ links, linkThickness, linkColor }: NetworkLinksProps) => {
25-
const { animate, config: springConfig } = useMotionConfig()
35+
const getExitTransition = <N extends NetworkInputNode>(
36+
linkColor: NetworkLinksProps<N>['linkColor']
37+
) => (link: ComputedLink<N>) => ({
38+
x1: link.source.x,
39+
y1: link.source.y,
40+
x2: link.source.x,
41+
y2: link.source.y,
42+
color: linkColor(link),
43+
opacity: 0,
44+
})
2645

27-
console.log('immediate', !animate)
46+
export const NetworkLinks = <N extends NetworkInputNode>({
47+
links,
48+
linkThickness,
49+
linkColor,
50+
}: NetworkLinksProps<N>) => {
51+
const { animate, config: springConfig } = useMotionConfig()
2852

29-
const regularTransition = useMemo(() => getRegularTransition(linkColor), [linkColor])
53+
const [enterTransition, regularTransition, exitTransition] = useMemo(
54+
() => [
55+
getEnterTransition<N>(linkColor),
56+
getRegularTransition<N>(linkColor),
57+
getExitTransition<N>(linkColor),
58+
],
59+
[linkColor]
60+
)
3061

3162
const transition = useTransition<
32-
ComputedLink,
63+
ComputedLink<N>,
3364
{
3465
x1: number
3566
y1: number
@@ -41,24 +72,10 @@ export const NetworkLinks = ({ links, linkThickness, linkColor }: NetworkLinksPr
4172
>(links, {
4273
keys: link => link.id,
4374
initial: regularTransition,
44-
from: link => ({
45-
x1: link.source.x,
46-
y1: link.source.y,
47-
x2: link.target.x,
48-
y2: link.target.y,
49-
color: linkColor(link),
50-
opacity: 0,
51-
}),
75+
from: enterTransition,
5276
enter: regularTransition,
5377
update: regularTransition,
54-
leave: link => ({
55-
x1: link.source.x,
56-
y1: link.source.y,
57-
x2: link.source.x,
58-
y2: link.source.y,
59-
color: linkColor(link),
60-
opacity: 0,
61-
}),
78+
leave: exitTransition,
6279
expires: true,
6380
config: springConfig,
6481
immediate: !animate,
+4-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { BasicTooltip } from '@nivo/tooltip'
2-
import { NetworkNodeTooltipProps } from './types'
2+
import { NetworkInputNode, NetworkNodeTooltipProps } from './types'
33

4-
export const NetworkNodeTooltip = ({ node }: NetworkNodeTooltipProps) => (
5-
<BasicTooltip
6-
id={node.id}
7-
enableChip={true}
8-
color={node.color}
9-
// renderContent={typeof tooltip === 'function' ? tooltip.bind(null, { ...node }) : null}
10-
/>
11-
)
4+
export const NetworkNodeTooltip = <N extends NetworkInputNode>({
5+
node,
6+
}: NetworkNodeTooltipProps<N>) => <BasicTooltip id={node.id} enableChip={true} color={node.color} />

‎packages/network/src/defaults.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { NetworkNode } from './NetworkNode'
22
import { NetworkNodeTooltip } from './NetworkNodeTooltip'
33
import { NetworkLayerId } from './types'
44

5-
const commonDefaultProps = {
5+
export const commonDefaultProps = {
66
layers: ['links', 'nodes'] as NetworkLayerId[],
77

88
linkDistance: 30,
@@ -27,18 +27,12 @@ const commonDefaultProps = {
2727
role: 'img',
2828
}
2929

30-
export const NetworkDefaultProps = {
31-
...commonDefaultProps,
32-
}
33-
3430
export const svgDefaultProps = {
35-
...NetworkDefaultProps,
31+
...commonDefaultProps,
3632
nodeComponent: NetworkNode,
3733
}
3834

39-
export const NetworkCanvasDefaultProps = {
35+
export const canvasDefaultProps = {
4036
...commonDefaultProps,
4137
pixelRatio: typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1,
4238
}
43-
44-
export const canvasDefaultProps = NetworkCanvasDefaultProps

‎packages/network/src/hooks.ts

+29-16
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ import get from 'lodash/get'
33
import isString from 'lodash/isString'
44
import isNumber from 'lodash/isNumber'
55
import { forceSimulation, forceManyBody, forceCenter, forceLink } from 'd3-force'
6-
import { InputLink, NetworkInputNode, NetworkCommonProps, NetworkNodeColor } from './types'
6+
import {
7+
InputLink,
8+
NetworkInputNode,
9+
NetworkCommonProps,
10+
NetworkNodeColor,
11+
NetworkLinkThickness,
12+
NetworkComputedNode,
13+
ComputedLink,
14+
} from './types'
715

816
const computeForces = <N extends NetworkInputNode>({
917
linkDistance,
@@ -18,18 +26,18 @@ const computeForces = <N extends NetworkInputNode>({
1826
distanceMax: NetworkCommonProps<N>['distanceMax']
1927
center: [number, number]
2028
}) => {
21-
let computedLinkDistance
29+
let getLinkDistance
2230
if (typeof linkDistance === 'function') {
23-
computedLinkDistance = linkDistance
31+
getLinkDistance = linkDistance
2432
} else if (isNumber(linkDistance)) {
25-
computedLinkDistance = linkDistance
33+
getLinkDistance = linkDistance
2634
} else if (isString(linkDistance)) {
27-
computedLinkDistance = link => get(link, linkDistance)
35+
getLinkDistance = (link: InputLink) => get(link, linkDistance)
2836
}
2937

3038
const linkForce = forceLink()
31-
.id(d => d.id)
32-
.distance(computedLinkDistance)
39+
.id((d: any) => d.id)
40+
.distance(getLinkDistance as any)
3341

3442
const chargeForce = forceManyBody()
3543
.strength(-repulsivity)
@@ -59,9 +67,9 @@ export const useNetwork = <N extends NetworkInputNode = NetworkInputNode>({
5967
distanceMax: NetworkCommonProps<N>['distanceMax']
6068
center: [number, number]
6169
iterations: NetworkCommonProps<N>['iterations']
62-
}) => {
63-
const [currentNodes, setCurrentNodes] = useState([])
64-
const [currentLinks, setCurrentLinks] = useState([])
70+
}): [null | NetworkComputedNode<N>[], null | ComputedLink<N>[]] => {
71+
const [currentNodes, setCurrentNodes] = useState<null | NetworkComputedNode<N>[]>(null)
72+
const [currentLinks, setCurrentLinks] = useState<null | ComputedLink<N>[]>(null)
6573

6674
useEffect(() => {
6775
const forces = computeForces<N>({
@@ -78,19 +86,24 @@ export const useNetwork = <N extends NetworkInputNode = NetworkInputNode>({
7886
...link,
7987
}))
8088

81-
const simulation = forceSimulation(nodesCopy)
89+
const simulation = forceSimulation(nodesCopy as any[])
8290
.force('link', forces.link.links(linksCopy))
8391
.force('charge', forces.charge)
8492
.force('center', forces.center)
8593
.stop()
8694

8795
simulation.tick(iterations)
8896

89-
setCurrentNodes(nodesCopy)
97+
// d3 mutates data, hence the castings
98+
setCurrentNodes((nodesCopy as unknown) as NetworkComputedNode<N>[])
9099
setCurrentLinks(
91-
linksCopy.map(link => {
92-
link.previousSource = currentNodes.find(n => n.id === link.source.id)
93-
link.previousTarget = currentNodes.find(n => n.id === link.target.id)
100+
((linksCopy as unknown) as ComputedLink<N>[]).map(link => {
101+
link.previousSource = currentNodes
102+
? currentNodes.find(n => n.id === link.source.id)
103+
: undefined
104+
link.previousTarget = currentNodes
105+
? currentNodes.find(n => n.id === link.target.id)
106+
: undefined
94107

95108
return link
96109
})
@@ -120,7 +133,7 @@ export const useNodeColor = <N extends NetworkInputNode>(color: NetworkNodeColor
120133
return () => color
121134
}, [color])
122135

123-
export const useLinkThickness = thickness =>
136+
export const useLinkThickness = <N extends NetworkInputNode>(thickness: NetworkLinkThickness<N>) =>
124137
useMemo(() => {
125138
if (typeof thickness === 'function') return thickness
126139
return () => thickness

‎packages/network/src/types.ts

+18-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AriaAttributes, MouseEvent, FunctionComponent } from 'react'
22
import { AnimatedProps } from '@react-spring/web'
3-
import { Box, Theme, Dimensions, ModernMotionProps, PropertyAccessor } from '@nivo/core'
3+
import { Box, Theme, Dimensions, ModernMotionProps } from '@nivo/core'
44
import { InheritedColorConfig } from '@nivo/colors'
55

66
export interface NetworkInputNode {
@@ -78,6 +78,10 @@ export interface NetworkCustomLayerProps<N extends NetworkInputNode> {
7878
export type NetworkCustomLayer<N extends NetworkInputNode> = FunctionComponent<
7979
NetworkCustomLayerProps<N>
8080
>
81+
export type NetworkCustomCanvasLayer<N extends NetworkInputNode> = (
82+
ctx: CanvasRenderingContext2D,
83+
props: NetworkCustomLayerProps<N>
84+
) => void
8185

8286
export interface NetworkNodeTooltipProps<N extends NetworkInputNode> {
8387
node: NetworkComputedNode<N>
@@ -91,12 +95,19 @@ export type NetworkNodeColor<N extends NetworkInputNode> =
9195
| string
9296
| ((node: NetworkComputedNode<N>) => string)
9397

98+
// support static distance, a property access if passing a string
99+
// or a dynamic function receiving the link.
100+
export type NetworkLinkDistance = number | string | ((link: InputLink) => number)
101+
102+
// support static thickness or a dynamic function receiving the link
103+
export type NetworkLinkThickness<N extends NetworkInputNode> =
104+
| number
105+
| ((link: ComputedLink<N>) => number)
106+
94107
export interface NetworkCommonProps<N extends NetworkInputNode> {
95108
margin: Box
96109

97-
layers: (NetworkLayerId | NetworkCustomLayer<N>)[]
98-
99-
linkDistance: number | PropertyAccessor<InputLink, number>
110+
linkDistance: NetworkLinkDistance
100111
repulsivity: number
101112
distanceMin: number
102113
distanceMax: number
@@ -108,7 +119,7 @@ export interface NetworkCommonProps<N extends NetworkInputNode> {
108119
nodeBorderWidth: number
109120
nodeBorderColor: InheritedColorConfig<NetworkComputedNode<N>>
110121

111-
linkThickness: number | PropertyAccessor<ComputedLink<N>, number>
122+
linkThickness: NetworkLinkThickness<N>
112123
linkColor: InheritedColorConfig<ComputedLink<N>>
113124

114125
isInteractive: boolean
@@ -127,6 +138,7 @@ export type NetworkSvgProps<N extends NetworkInputNode> = Partial<NetworkCommonP
127138
NetworkDataProps<N> &
128139
Dimensions &
129140
ModernMotionProps & {
141+
layers?: (NetworkLayerId | NetworkCustomLayer<N>)[]
130142
nodeComponent?: NetworkNodeComponent<N>
131143
}
132144

@@ -135,5 +147,6 @@ export type NetworkCanvasProps<N extends NetworkInputNode> = Partial<NetworkComm
135147
Dimensions &
136148
// only used by tooltips
137149
ModernMotionProps & {
150+
layers?: (NetworkLayerId | NetworkCustomCanvasLayer<N>)[]
138151
pixelRatio?: number
139152
}

‎website/src/data/components/network/props.ts

+57-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// @ts-ignore
2-
import { NetworkDefaultProps } from '@nivo/network'
1+
import { commonDefaultProps } from '@nivo/network'
32
import { motionProperties, groupProperties, themeProperty } from '../../../lib/componentProperties'
43
import { chartDimensions, pixelRatio } from '../../../lib/chart-properties'
54
import { ChartProperty, Flavor } from '../../../types'
@@ -10,7 +9,7 @@ const props: ChartProperty[] = [
109
{
1110
key: 'data',
1211
group: 'Base',
13-
type: '{ nodes: object[], links: object[] }',
12+
type: '{ nodes: NetworkInputNode[], links: NetworkInputLink[] }',
1413
required: true,
1514
help: 'Chart data defining nodes and links.',
1615
flavors: allFlavors,
@@ -67,25 +66,35 @@ const props: ChartProperty[] = [
6766
min: 1,
6867
max: 100,
6968
},
70-
defaultValue: NetworkDefaultProps.repulsivity,
69+
defaultValue: commonDefaultProps.repulsivity,
7170
},
7271
{
7372
key: 'distanceMin',
7473
group: 'Simulation',
7574
type: 'number',
7675
required: false,
7776
help: 'Sets the minimum distance between nodes for the many-body force.',
77+
<<<<<<< HEAD
7878
flavors: allFlavors,
7979
defaultValue: NetworkDefaultProps.distanceMin,
80+
=======
81+
flavors: ['svg', 'canvas'],
82+
defaultValue: commonDefaultProps.distanceMin,
83+
>>>>>>> feat(network): types are now valid
8084
},
8185
{
8286
key: 'distanceMax',
8387
group: 'Simulation',
8488
type: 'number',
8589
required: false,
8690
help: 'Sets the maximum disteance between nodes for the many-body force.',
91+
<<<<<<< HEAD
8792
flavors: allFlavors,
8893
defaultValue: NetworkDefaultProps.distanceMax,
94+
=======
95+
flavors: ['svg', 'canvas'],
96+
defaultValue: commonDefaultProps.distanceMax,
97+
>>>>>>> feat(network): types are now valid
8998
},
9099
{
91100
key: 'iterations',
@@ -97,10 +106,17 @@ const props: ChartProperty[] = [
97106
`,
98107
type: 'number',
99108
required: false,
109+
<<<<<<< HEAD
100110
defaultValue: NetworkDefaultProps.iterations,
101111
flavors: allFlavors,
102112
control: {
103113
type: 'range',
114+
=======
115+
defaultValue: commonDefaultProps.iterations,
116+
flavors: ['svg', 'canvas'],
117+
controlType: 'range',
118+
controlOptions: {
119+
>>>>>>> feat(network): types are now valid
104120
min: 60,
105121
max: 260,
106122
},
@@ -112,50 +128,80 @@ const props: ChartProperty[] = [
112128
type: 'string | (node: InputNode) => string',
113129
required: false,
114130
help: `Control nodes' color.`,
131+
<<<<<<< HEAD
115132
flavors: allFlavors,
116133
defaultValue: NetworkDefaultProps.nodeColor,
134+
=======
135+
flavors: ['svg', 'canvas'],
136+
defaultValue: commonDefaultProps.nodeColor,
137+
>>>>>>> feat(network): types are now valid
117138
},
118139
{
119140
key: 'nodeBorderWidth',
120141
group: 'Nodes',
121-
type: 'number | (node: Node) => number',
142+
type: 'number | (node: NetworkComputedNode) => number',
122143
required: false,
123144
help: `Control nodes' border width.`,
145+
<<<<<<< HEAD
124146
defaultValue: NetworkDefaultProps.nodeBorderWidth,
125147
flavors: allFlavors,
126148
control: { type: 'lineWidth' },
149+
=======
150+
defaultValue: commonDefaultProps.nodeBorderWidth,
151+
flavors: ['svg', 'canvas'],
152+
controlType: 'lineWidth',
153+
>>>>>>> feat(network): types are now valid
127154
},
128155
{
129156
key: 'nodeBorderColor',
130157
group: 'Nodes',
131-
type: 'string | object | (link: Link) => string',
158+
type: 'InheritedColorConfig<NetworkComputedNode>',
132159
required: false,
133160
help: `Control nodes' border color.`,
161+
<<<<<<< HEAD
134162
defaultValue: NetworkDefaultProps.nodeBorderColor,
135163
flavors: allFlavors,
136164
control: { type: 'inheritedColor' },
165+
=======
166+
defaultValue: commonDefaultProps.nodeBorderColor,
167+
flavors: ['svg', 'canvas'],
168+
controlType: 'inheritedColor',
169+
>>>>>>> feat(network): types are now valid
137170
},
138171
{
139172
key: 'linkThickness',
140173
enableControlForFlavors: ['canvas'],
141174
group: 'Links',
142-
type: 'number | (link: Link) => number',
175+
type: 'number | (link: NetworkComputedLink) => number',
143176
required: false,
144177
help: `Control links' thickness.`,
178+
<<<<<<< HEAD
145179
flavors: allFlavors,
146180
defaultValue: NetworkDefaultProps.linkThickness,
147181
control: { type: 'lineWidth' },
182+
=======
183+
flavors: ['svg', 'canvas'],
184+
defaultValue: commonDefaultProps.linkThickness,
185+
controlType: 'lineWidth',
186+
>>>>>>> feat(network): types are now valid
148187
},
149188
{
150189
key: 'linkColor',
151190
group: 'Links',
152-
type: 'string | (link: Link) => string',
191+
type: 'InheritedColorConfig<NetworkComputedLink>',
153192
required: false,
154193
help: `Control links' color.`,
194+
<<<<<<< HEAD
155195
defaultValue: NetworkDefaultProps.linkColor,
156196
flavors: allFlavors,
157197
control: {
158198
type: 'inheritedColor',
199+
=======
200+
defaultValue: commonDefaultProps.linkColor,
201+
flavors: ['svg', 'canvas'],
202+
controlType: 'inheritedColor',
203+
controlOptions: {
204+
>>>>>>> feat(network): types are now valid
159205
inheritableProperties: ['source.color', 'target.color'],
160206
},
161207
},
@@ -166,7 +212,7 @@ const props: ChartProperty[] = [
166212
required: false,
167213
help: 'Enable/disable interactivity.',
168214
flavors: ['svg'],
169-
defaultValue: NetworkDefaultProps.isInteractive,
215+
defaultValue: commonDefaultProps.isInteractive,
170216
controlType: 'switch',
171217
},
172218
{
@@ -220,7 +266,7 @@ const props: ChartProperty[] = [
220266
group: 'Customization',
221267
help: 'Defines the order of layers and add custom layers.',
222268
required: false,
223-
defaultValue: NetworkDefaultProps.layers,
269+
defaultValue: commonDefaultProps.layers,
224270
flavors: ['svg', 'canvas'],
225271
},
226272
{
@@ -255,7 +301,7 @@ const props: ChartProperty[] = [
255301
help: 'Main element [aria-describedby](https://www.w3.org/TR/wai-aria/#aria-describedby).',
256302
flavors: ['svg'],
257303
},
258-
...motionProperties(['svg'], NetworkDefaultProps, 'react-spring'),
304+
...motionProperties(['svg'], commonDefaultProps, 'react-spring'),
259305
]
260306

261307
export const groups = groupProperties(props)

‎website/src/pages/network/canvas.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
2-
import { ResponsiveNetworkCanvas, NetworkCanvasDefaultProps } from '@nivo/network'
2+
import { ResponsiveNetworkCanvas, canvasDefaultProps } from '@nivo/network'
33
import { ComponentTemplate } from '../../components/components/ComponentTemplate'
44
import meta from '../../data/components/network/meta.yml'
55
import { groups } from '../../data/components/network/props'
@@ -25,7 +25,7 @@ const initialProperties = Object.freeze({
2525
nodeBorderWidth: 1,
2626
nodeBorderColor: { theme: 'background' },
2727

28-
linkColor: NetworkCanvasDefaultProps.linkColor,
28+
linkColor: canvasDefaultProps.linkColor,
2929
linkThickness: 1,
3030

3131
isInteractive: true,
@@ -57,7 +57,7 @@ const NetworkCanvas = () => {
5757
currentFlavor="canvas"
5858
properties={groups}
5959
initialProperties={initialProperties}
60-
defaultProperties={NetworkCanvasDefaultProps}
60+
defaultProperties={canvasDefaultProps}
6161
generateData={() =>
6262
generateData({
6363
rootNodeRadius: 10,

‎website/src/pages/network/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
2-
import { ResponsiveNetwork, NetworkDefaultProps } from '@nivo/network'
2+
import { ResponsiveNetwork, svgDefaultProps } from '@nivo/network'
33
import { ComponentTemplate } from '../../components/components/ComponentTemplate'
44
import meta from '../../data/components/network/meta.yml'
55
import { groups } from '../../data/components/network/props'
@@ -25,7 +25,7 @@ const initialProperties = Object.freeze({
2525
modifiers: [['darker', 0.8]],
2626
},
2727

28-
linkColor: NetworkDefaultProps.linkColor,
28+
linkColor: svgDefaultProps.linkColor,
2929
linkThickness: link => (2 - link.source.depth) * 2,
3030

3131
isInteractive: true,
@@ -60,7 +60,7 @@ const Network = () => {
6060
currentFlavor="svg"
6161
properties={groups}
6262
initialProperties={initialProperties}
63-
defaultProperties={NetworkDefaultProps}
63+
defaultProperties={svgDefaultProps}
6464
generateData={generateData}
6565
getDataSize={data => data.nodes.length}
6666
image={image}

0 commit comments

Comments
 (0)
Please sign in to comment.