diff --git a/Makefile b/Makefile index 78dede83a0..df69411bae 100644 --- a/Makefile +++ b/Makefile @@ -223,6 +223,7 @@ website: ##@2 website start website in dev mode website-build: ##@2 website build website @echo "${YELLOW}Building website${RESET}" + @-rm -rf website/.cache @cd website && yarn build website-serve: ##@2 website build & serve website diff --git a/packages/generators/src/network.ts b/packages/generators/src/network.ts index 6e433d2015..0cd6f3bbe4 100644 --- a/packages/generators/src/network.ts +++ b/packages/generators/src/network.ts @@ -1,37 +1,37 @@ import random from 'lodash/random' type Link = { - distance: number source: string target: string + distance: number } type ExtraNode = { - color: string - depth: number id: string - radius: number + height: number + color: string + size: number } export const generateNetworkData = ({ - rootNodeRadius = 12, + rootNodeSize = 24, minMidNodes = 6, maxMidNodes = 16, - midNodeRadius = 8, + midNodeSize = 16, minLeaves = 4, maxLeaves = 16, - leafRadius = 4, + leafSize = 8, } = {}) => { const rootNode = { id: '0', - radius: rootNodeRadius, - depth: 0, + height: 2, + size: rootNodeSize, color: 'rgb(244, 117, 96)', } let nodes = Array.from({ length: random(minMidNodes, maxMidNodes) }, (_, k) => ({ id: `${k + 1}`, - radius: midNodeRadius, - depth: 1, + height: 1, + size: midNodeSize, color: 'rgb(97, 205, 187)', })) @@ -41,28 +41,28 @@ export const generateNetworkData = ({ links.push({ source: '0', target: source.id, - distance: 50, + distance: 80, }) nodes.forEach(target => { if (Math.random() < 0.04) { links.push({ source: source.id, target: target.id, - distance: 70, + distance: 80, }) } }) Array.from({ length: random(minLeaves, maxLeaves) }, (_, k) => { extraNodes.push({ id: `${source.id}.${k}`, - radius: leafRadius, - depth: 2, + height: 0, + size: leafSize, color: 'rgb(232, 193, 160)', }) links.push({ source: source.id, target: `${source.id}.${k}`, - distance: 30, + distance: 50, }) return null diff --git a/packages/network/src/Network.tsx b/packages/network/src/Network.tsx index 3a96706322..f547efcac8 100644 --- a/packages/network/src/Network.tsx +++ b/packages/network/src/Network.tsx @@ -21,6 +21,7 @@ const InnerNetwork = ({ data: { nodes: rawNodes, links: rawLinks }, linkDistance = svgDefaultProps.linkDistance, + centeringStrength = svgDefaultProps.centeringStrength, repulsivity = svgDefaultProps.repulsivity, distanceMin = svgDefaultProps.distanceMin, distanceMax = svgDefaultProps.distanceMax, @@ -70,6 +71,7 @@ const InnerNetwork = ({ nodes: rawNodes, links: rawLinks, linkDistance, + centeringStrength, repulsivity, distanceMin, distanceMax, diff --git a/packages/network/src/NetworkCanvas.tsx b/packages/network/src/NetworkCanvas.tsx index 371d3b5f4a..96122a915e 100644 --- a/packages/network/src/NetworkCanvas.tsx +++ b/packages/network/src/NetworkCanvas.tsx @@ -19,6 +19,7 @@ const InnerNetworkCanvas = ({ data: { nodes: rawNodes, links: rawLinks }, linkDistance = canvasDefaultProps.linkDistance, + centeringStrength = canvasDefaultProps.centeringStrength, repulsivity = canvasDefaultProps.repulsivity, distanceMin = canvasDefaultProps.distanceMin, distanceMax = canvasDefaultProps.distanceMax, @@ -54,6 +55,7 @@ const InnerNetworkCanvas = ({ nodes: rawNodes, links: rawLinks, linkDistance, + centeringStrength, repulsivity, distanceMin, distanceMax, diff --git a/packages/network/src/defaults.ts b/packages/network/src/defaults.ts index fc966d3d8f..a6db410f62 100644 --- a/packages/network/src/defaults.ts +++ b/packages/network/src/defaults.ts @@ -22,10 +22,11 @@ export const commonDefaultProps: Omit< layers: ['links', 'nodes', 'annotations'], linkDistance: 30, - repulsivity: 3, + centeringStrength: 1, + repulsivity: 10, distanceMin: 1, distanceMax: Infinity, - iterations: 160, + iterations: 120, nodeSize: 12, activeNodeSize: 18, diff --git a/packages/network/src/hooks.ts b/packages/network/src/hooks.ts index b3450fdb53..8da7f078e0 100644 --- a/packages/network/src/hooks.ts +++ b/packages/network/src/hooks.ts @@ -25,12 +25,14 @@ const useDerivedProp = ( const useComputeForces = ({ linkDistance, + centeringStrength, repulsivity, distanceMin, distanceMax, center, }: { linkDistance: NetworkCommonProps['linkDistance'] + centeringStrength: NetworkCommonProps['centeringStrength'] repulsivity: NetworkCommonProps['repulsivity'] distanceMin: NetworkCommonProps['distanceMin'] distanceMax: NetworkCommonProps['distanceMax'] @@ -42,9 +44,9 @@ const useComputeForces = ({ const centerY = center[1] return useMemo(() => { - const linkForce = forceLink, TransientLink>().distance( - link => getLinkDistance(link.data) - ) + const linkForce = forceLink, TransientLink>() + .distance(link => getLinkDistance(link.data)) + .strength(centeringStrength) const chargeForce = forceManyBody() .strength(-repulsivity) @@ -54,7 +56,15 @@ const useComputeForces = ({ const centerForce = forceCenter(centerX, centerY) return { link: linkForce, charge: chargeForce, center: centerForce } - }, [getLinkDistance, repulsivity, distanceMin, distanceMax, centerX, centerY]) + }, [ + getLinkDistance, + centeringStrength, + repulsivity, + distanceMin, + distanceMax, + centerX, + centerY, + ]) } const useNodeStyle = ({ @@ -141,6 +151,7 @@ export const useNetwork = ['linkDistance'] + centeringStrength?: NetworkCommonProps['centeringStrength'] repulsivity?: NetworkCommonProps['repulsivity'] distanceMin?: NetworkCommonProps['distanceMin'] distanceMax?: NetworkCommonProps['distanceMax'] @@ -180,6 +192,7 @@ export const useNetwork = ({ linkDistance, + centeringStrength, repulsivity, distanceMin, distanceMax, diff --git a/packages/network/src/types.ts b/packages/network/src/types.ts index 16aac38a44..34b5f9e14a 100644 --- a/packages/network/src/types.ts +++ b/packages/network/src/types.ts @@ -139,6 +139,7 @@ export type NetworkCommonProps = margin: Box linkDistance: DerivedProp + centeringStrength: number repulsivity: number distanceMin: number distanceMax: number diff --git a/website/src/components/icons/NetworkIcon.tsx b/website/src/components/icons/NetworkIcon.tsx index 4c04d496b4..c5a6b536ed 100644 --- a/website/src/components/icons/NetworkIcon.tsx +++ b/website/src/components/icons/NetworkIcon.tsx @@ -7,24 +7,30 @@ import networkDarkColoredImg from '../../assets/icons/network-dark-colored.png' import { ICON_SIZE, Icon, colors, IconImg } from './styled' import { IconType } from './types' +type Node = { + id: string + size: number + color: string +} + const getData = (currentColors: any) => { let nodes = 'ABCDE'.split('').map(id => ({ id, - radius: 5, + size: 10, color: currentColors[2], })) const links = nodes.map(node => ({ source: 'root', target: node.id, - distance: 2, + distance: 30, })) - const leaves: any[] = [] + const leaves: Node[] = [] nodes.forEach(node => { Array.from({ length: 7 }, (v, k) => { leaves.push({ id: `${node.id}.${k}`, - radius: 3, + size: 6, color: currentColors[4], }) links.push({ @@ -36,18 +42,22 @@ const getData = (currentColors: any) => { }) nodes = nodes.concat(leaves) - nodes.unshift({ id: 'root', radius: 11, color: currentColors[4] }) + nodes.unshift({ id: 'root', size: 20, color: currentColors[4] }) return { nodes, links } } +type Link = ReturnType['links'][number] + const chartProps = { width: ICON_SIZE, height: ICON_SIZE, - linkDistance: (link: any) => link.distance, - repulsivity: 5, + linkDistance: (link: Link) => link.distance, + centeringStrength: 1.2, + repulsivity: 4, linkThickness: 2, - nodeColor: (node: any) => node.color, + nodeSize: (node: Node) => node.size, + nodeColor: (node: Node) => node.color, linkColor: { from: 'source.color' }, animate: false, isInteractive: false, @@ -55,7 +65,7 @@ const chartProps = { const NetworkIconItem = ({ type }: { type: IconType }) => ( - + {...chartProps} data={getData(colors[type].colors)} /> ) diff --git a/website/src/data/components/network/mapper.ts b/website/src/data/components/network/mapper.ts index 1f5ac3a933..848481198d 100644 --- a/website/src/data/components/network/mapper.ts +++ b/website/src/data/components/network/mapper.ts @@ -1,28 +1,28 @@ import { settingsMapper } from '../../../lib/settings' -export const dynamicNodeSizeValue = 'dynamic: (node: InputNode) => node.radius * 2' -export const dynamicActiveNodeSizeValue = 'dynamic: (node: InputNode) => node.radius * 3' +export const dynamicNodeSizeValue = 'dynamic: (node: InputNode) => node.size' +export const dynamicActiveNodeSizeValue = 'dynamic: (node: InputNode) => node.size * 1.5' export const dynamicLinkThicknessValue = 'dynamic: (link: ComputedLink) => (2 - link.source.data.depth) * 2' export default settingsMapper({ nodeSize: (value: number | typeof dynamicNodeSizeValue) => { if (value === dynamicNodeSizeValue) { - return (node: any) => node.radius * 2 + return (node: any) => node.size } return value }, activeNodeSize: (value: number | typeof dynamicActiveNodeSizeValue) => { if (value === dynamicActiveNodeSizeValue) { - return (node: any) => node.radius * 3 + return (node: any) => node.size * 1.5 } return value }, linkThickness: (value: number | typeof dynamicLinkThicknessValue) => { if (value === dynamicLinkThicknessValue) { - return (link: any) => (2 - link.source.data.depth) * 2 + return (link: any) => 2 + link.target.data.height * 2 } return value diff --git a/website/src/data/components/network/meta.yml b/website/src/data/components/network/meta.yml index 9098ba5d9b..d791e7330e 100644 --- a/website/src/data/components/network/meta.yml +++ b/website/src/data/components/network/meta.yml @@ -17,7 +17,12 @@ Network: - label: Custom link component link: network--custom-link description: | - A network component connecting nodes with links. + A network component connecting nodes with links using various forces, + the resulting layout will depends on `linkDistance`, `centeringStrength` + and `repulsivity`, so you should play with those parameters in order + to achieve the desired result. + + You can also add some extra constrains via `distanceMin` and `distanceMax`. The responsive alternative of this component is `ResponsiveNetwork`. diff --git a/website/src/data/components/network/props.ts b/website/src/data/components/network/props.ts index c4db3b1c16..7f6885ebbb 100644 --- a/website/src/data/components/network/props.ts +++ b/website/src/data/components/network/props.ts @@ -52,13 +52,30 @@ const props: ChartProperty[] = [ description: ` If you set a **number**, this value will be used for all links. - If you use a **string**, this will be used to pick the distance - from the corresponding link property, thus, this property - should exist on each link. - If you use a **function**, it will receive a link and must return the desired distance. + + Please note that in most cases you won't get links having the + exact distance you specified as it also depends on the other forces. + `, + }, + { + key: 'centeringStrength', + group: 'Simulation', + type: 'number', + help: 'Control how much the centering force affects nodes positioning.', + description: ` + This value will also affect the strength + of \`distanceMin\` and \`distanceMax\`. `, + flavors: allFlavors, + defaultValue: defaults.centeringStrength, + control: { + type: 'range', + min: 0, + max: 2, + step: 0.1, + }, }, { key: 'repulsivity', diff --git a/website/src/pages/network/canvas.tsx b/website/src/pages/network/canvas.tsx index fe35f39c3f..d7ce744841 100644 --- a/website/src/pages/network/canvas.tsx +++ b/website/src/pages/network/canvas.tsx @@ -4,7 +4,7 @@ import { ResponsiveNetworkCanvas, canvasDefaultProps as defaults } from '@nivo/n import { generateNetworkData } from '@nivo/generators' import { ComponentTemplate } from '../../components/components/ComponentTemplate' import meta from '../../data/components/network/meta.yml' -import mapper from '../../data/components/network/mapper' +import mapper, { dynamicLinkThicknessValue } from '../../data/components/network/mapper' import { groups } from '../../data/components/network/props' const initialProperties = Object.freeze({ @@ -18,9 +18,10 @@ const initialProperties = Object.freeze({ left: 0, }, - linkDistance: 'distance', - repulsivity: 4, - iterations: 60, + linkDistance: (link: any) => link.distance, + centeringStrength: 0.3, + repulsivity: 6, + iterations: defaults.iterations, nodeSize: defaults.nodeSize, activeNodeSize: defaults.activeNodeSize, @@ -29,8 +30,8 @@ const initialProperties = Object.freeze({ nodeBorderWidth: 1, nodeBorderColor: { theme: 'background' }, + linkThickness: dynamicLinkThicknessValue, linkColor: defaults.linkColor, - linkThickness: defaults.linkThickness, annotations: defaults.annotations, diff --git a/website/src/pages/network/index.tsx b/website/src/pages/network/index.tsx index cd1a881f2c..8153edee6b 100644 --- a/website/src/pages/network/index.tsx +++ b/website/src/pages/network/index.tsx @@ -23,8 +23,8 @@ const initialProperties = Object.freeze({ }, linkDistance: (link: Link) => link.distance, - distanceMax: 50, - repulsivity: defaults.repulsivity, + centeringStrength: 0.3, + repulsivity: 6, iterations: defaults.iterations, nodeSize: dynamicNodeSizeValue,