From a47e83a5b18a26579d019cbf6bf22390d4812ea6 Mon Sep 17 00:00:00 2001 From: Nikita Rokotyan Date: Fri, 5 Jan 2024 11:14:13 -0800 Subject: [PATCH] Dev | Examples | Graph: SVG Icons, curved links, long labels #319 #320 #321 --- packages/dev/src/declarations.d.ts | 5 ++ .../graph/graph-custom-icons/bucket.svg | 3 + .../graph/graph-custom-icons/index.module.css | 7 ++ .../graph/graph-custom-icons/index.tsx | 68 +++++++++++++++++++ .../graph/graph-custom-icons/instance.svg | 5 ++ .../graph/graph-custom-icons/person.svg | 10 +++ .../graph/graph-custom-icons/role.svg | 3 + packages/dev/webpack.config.js | 4 ++ .../components/graph/modules/node/index.ts | 4 +- 9 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/bucket.svg create mode 100644 packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/index.module.css create mode 100644 packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/index.tsx create mode 100644 packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/instance.svg create mode 100644 packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/person.svg create mode 100644 packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/role.svg diff --git a/packages/dev/src/declarations.d.ts b/packages/dev/src/declarations.d.ts index d1c3d9ed5..259239fc0 100644 --- a/packages/dev/src/declarations.d.ts +++ b/packages/dev/src/declarations.d.ts @@ -2,3 +2,8 @@ declare module '*.module.css' { const classes: { [key: string]: string } export default classes } + +declare module '*.svg?raw' { + const content: string + export default content +} diff --git a/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/bucket.svg b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/bucket.svg new file mode 100644 index 000000000..d2d024def --- /dev/null +++ b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/bucket.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/index.module.css b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/index.module.css new file mode 100644 index 000000000..ffc1e4003 --- /dev/null +++ b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/index.module.css @@ -0,0 +1,7 @@ +.graph { + --vis-graph-node-label-font-size: 8pt; + --vis-graph-node-label-text-color: #717179; + + --vis-graph-node-sublabel-font-size: 9pt; + --vis-graph-node-sublabel-text-color: #000000; +} diff --git a/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/index.tsx b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/index.tsx new file mode 100644 index 000000000..d8ee9d374 --- /dev/null +++ b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/index.tsx @@ -0,0 +1,68 @@ +import React from 'react' +import { VisSingleContainer, VisGraph } from '@unovis/react' +import { NodeDatum } from '@src/utils/data' + +import personIcon from './person.svg?raw' +import roleIcon from './role.svg?raw' +import instanceIcon from './instance.svg?raw' +import bucketIcon from './bucket.svg?raw' + +import s from './index.module.css' + +export const title = 'Graph: SVG Node Icons' +export const subTitle = 'cured links, long labels' + +export const component = (): JSX.Element => { + const svgDefs = ` + ${personIcon} + ${roleIcon} + ${instanceIcon} + ${bucketIcon} + ` + + const nodes = [ + { id: 'jdoe@acme.com', icon: '#personIcon', fillColor: '#DFFAFD', label: 'External User', sublabel: 'jdoe@acme.com' }, + { id: 'AWSReservedSSO_Something', icon: '#roleIcon', fillColor: '#E3DEFC', label: 'Role', sublabel: 'AWSReservedSSO_Something' }, + { id: 'i-0a1b2c3d4e5f6g7h8', icon: '#instanceIcon', fillColor: '#D0E1FC', label: 'EC2 Instance', sublabel: 'i-0a1b2c3d4e5f6g7h8' }, + { id: 'i-1a1b2c3d4e5f6g7h8', icon: '#instanceIcon', fillColor: '#D0E1FC', label: 'EC2 Instance', sublabel: 'i-1a1b2c3d4e5f6g7h8' }, + { id: 'my-bucket', icon: '#bucketIcon', fillColor: '#D4DBFB', label: 'S3 Bucket', sublabel: 'my-bucket' }, + { id: 'tests-ansible-ssm-file-transfer', icon: '#bucketIcon', fillColor: '#D4DBFB', label: 'S3 Bucket', sublabel: 'tests-ansible-ssm-file-transfer' }, + ] + + const links = [ + { source: 0, target: 1, label: { text: 'assume' } }, + { source: 1, target: 2, label: { text: '1' } }, + { source: 1, target: 3, label: { text: 'label' } }, + { source: 1, target: 4 }, + { source: 1, target: 5, label: { text: '2' } }, + ] + + const data = { nodes, links } + return ( +
+ + n.icon} + nodeIconSize={18} + nodeStroke={'none'} + nodeFill={(n: NodeDatum) => n.fillColor} + nodeLabel={(n: NodeDatum) => n.label} + nodeSubLabel={(n: NodeDatum) => n.sublabel} + layoutType='dagre' + dagreLayoutSettings={{ + rankdir: 'LR', + ranksep: 120, + nodesep: 20, + }} + linkBandWidth={6} + linkFlow={true} + linkCurvature={1} + linkArrow={'single'} + linkLabel={(l: typeof links[0]) => l.label} + /> + +
+ ) +} + diff --git a/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/instance.svg b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/instance.svg new file mode 100644 index 000000000..7dfa6895d --- /dev/null +++ b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/instance.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/person.svg b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/person.svg new file mode 100644 index 000000000..0b2c3d93d --- /dev/null +++ b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/person.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/role.svg b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/role.svg new file mode 100644 index 000000000..90dbf6051 --- /dev/null +++ b/packages/dev/src/examples/networks-and-flows/graph/graph-custom-icons/role.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/dev/webpack.config.js b/packages/dev/webpack.config.js index 4fa9e2bf4..3df4f9f34 100644 --- a/packages/dev/webpack.config.js +++ b/packages/dev/webpack.config.js @@ -48,6 +48,10 @@ module.exports = { }, ], }, + { + resourceQuery: /raw/, + type: 'asset/source', + }, ], }, resolve: { diff --git a/packages/ts/src/components/graph/modules/node/index.ts b/packages/ts/src/components/graph/modules/node/index.ts index 0e0155954..c04ea1cdd 100644 --- a/packages/ts/src/components/graph/modules/node/index.ts +++ b/packages/ts/src/components/graph/modules/node/index.ts @@ -216,6 +216,7 @@ export function updateNodes // Update Node Icon const nodeIconContent = getString(d, nodeIcon, d._index) const nodeIconSizeValue = getNumber(d, nodeIconSize, d._index) ?? 2.5 * Math.sqrt(nodeSizeValue) + const nodeIconColor = getNodeIconColor(d, nodeFill, d._index, selection.node()) icon.selectAll('*').remove() // Removing all children first if (isInternalHref(nodeIconContent)) { // If the icon is a href, we need to append a element and render the icon with it icon.append('use') @@ -224,12 +225,13 @@ export function updateNodes .attr('y', -nodeIconSizeValue / 2) .attr('width', nodeIconSizeValue) .attr('height', nodeIconSizeValue) + .style('fill', nodeIconColor) } else { // If the icon is a text, we need to append a element and render the icon as text icon .append('text') .style('font-size', `${nodeIconSizeValue}px`) .attr('dy', '0.1em') - .style('fill', getNodeIconColor(d, nodeFill, d._index, selection.node())) + .style('fill', nodeIconColor) .html(nodeIconContent) }