From 3a647e423d637577eca73d2d5405073cbdd39fff Mon Sep 17 00:00:00 2001 From: Luc Patiny Date: Fri, 11 Oct 2024 06:56:21 +0200 Subject: [PATCH] feat: arrow label can be aligned left and middle --- demo/Fragmentation.tsx | 5 +- .../__snapshots__/molecule.test.tsx.snap | 2 +- src/__tests__/molecule.test.tsx | 2 +- src/components/Arrow.tsx | 44 ++++-- src/components/CenteredText.tsx | 29 ---- src/components/MultilineText.tsx | 23 ++++ src/components/RightText.tsx | 27 ---- src/components/Text.tsx | 51 +++++++ src/data/getArrows.tsx | 5 +- test/index.html | 130 +++++++++--------- 10 files changed, 184 insertions(+), 134 deletions(-) delete mode 100644 src/components/CenteredText.tsx create mode 100644 src/components/MultilineText.tsx delete mode 100644 src/components/RightText.tsx create mode 100644 src/components/Text.tsx diff --git a/demo/Fragmentation.tsx b/demo/Fragmentation.tsx index 54f71a3..ea87877 100644 --- a/demo/Fragmentation.tsx +++ b/demo/Fragmentation.tsx @@ -16,12 +16,13 @@ const props = { nodeRenderer: moleculeRenderer, arrowRendererOptions: { getLabel: (node) => { - return node?.reaction?.label; + return `Hello\n${node?.reaction?.label}\nWorld`; }, + verticalPosition: 'top', getID: () => { return Math.random().toString(); }, - labelPosition: 'center', + horizontalPosition: 'left', }, nodeRendererOptions: { getID: () => { diff --git a/src/__tests__/__snapshots__/molecule.test.tsx.snap b/src/__tests__/__snapshots__/molecule.test.tsx.snap index b0171e1..0b5c496 100644 --- a/src/__tests__/__snapshots__/molecule.test.tsx.snap +++ b/src/__tests__/__snapshots__/molecule.test.tsx.snap @@ -685,5 +685,5 @@ exports[`render: Molecule 1`] = ` -" +" `; diff --git a/src/__tests__/molecule.test.tsx b/src/__tests__/molecule.test.tsx index b6da879..216327c 100644 --- a/src/__tests__/molecule.test.tsx +++ b/src/__tests__/molecule.test.tsx @@ -29,7 +29,7 @@ test('render: Molecule', () => { getLabel: (node) => { return node.transformation; }, - labelPosition: 'center', + horizontalPosition: 'center', }, positionOptions: { spacingHorizontal: 150, diff --git a/src/components/Arrow.tsx b/src/components/Arrow.tsx index 32ba306..d70401f 100644 --- a/src/components/Arrow.tsx +++ b/src/components/Arrow.tsx @@ -1,8 +1,7 @@ import { Point } from '../types/Point'; -import { CenteredText } from './CenteredText'; import { refX } from './MarkerDef.utils'; -import { RightText } from './RightText'; +import { Text } from './Text'; /** * Creates a simple arrow between 2 points. The arrow has one inflection point, which is the middle of the line. * The SVG must define a marker with id="arrowhead" for this to work. @@ -15,9 +14,17 @@ export function Arrow(props: { from: Point; to: Point; label: string; - labelPosition: 'center' | 'right'; + horizontalPosition: 'left' | 'center' | 'right'; + verticalPosition?: 'top' | 'bottom' | 'center'; }) { - const { id, from, to, label, labelPosition = 'center' } = props; + const { + id, + from, + to, + label, + horizontalPosition = 'center', + verticalPosition = 'center', + } = props; const middle = { x: (from.x + to.x) / 2, y: (from.y + to.y) / 2 }; const headInflectionPoint = { @@ -37,11 +44,32 @@ export function Arrow(props: { markerEnd: 'url(#arrowhead)', }} /> - {labelPosition === 'center' && ( - + {horizontalPosition === 'center' && ( + )} - {labelPosition === 'right' && ( - + {horizontalPosition === 'right' && ( + + )} + {horizontalPosition === 'left' && ( + )} ); diff --git a/src/components/CenteredText.tsx b/src/components/CenteredText.tsx deleted file mode 100644 index 0bdb327..0000000 --- a/src/components/CenteredText.tsx +++ /dev/null @@ -1,29 +0,0 @@ -export function CenteredText(props: { x: number; y: number; label: string }) { - const { x, y, label } = props; - const labelPosition = `translate(${x},${y})`; - return ( - <> - - {label} - - - {label} - - - ); -} diff --git a/src/components/MultilineText.tsx b/src/components/MultilineText.tsx new file mode 100644 index 0000000..f40c314 --- /dev/null +++ b/src/components/MultilineText.tsx @@ -0,0 +1,23 @@ +export function MultilineText(props: { + label: string; + verticalPosition: 'top' | 'center' | 'bottom'; +}) { + const { label, verticalPosition } = props; + if (!label) return; + const lines = label.split(/\r?\n/); + const firstDX = + verticalPosition === 'top' + ? 1 + : verticalPosition === 'center' + ? -lines.length / 2 + 0.5 + : -lines.length; + return ( + <> + {lines.map((line, index) => ( + + {line} + + ))} + + ); +} diff --git a/src/components/RightText.tsx b/src/components/RightText.tsx deleted file mode 100644 index 1d7a8f5..0000000 --- a/src/components/RightText.tsx +++ /dev/null @@ -1,27 +0,0 @@ -export function RightText(props: { x: number; y: number; label: string }) { - const { x, y, label } = props; - const labelPosition = `translate(${x},${y})`; - return ( - <> - - {label} - - - {label} - - - ); -} diff --git a/src/components/Text.tsx b/src/components/Text.tsx new file mode 100644 index 0000000..9e73856 --- /dev/null +++ b/src/components/Text.tsx @@ -0,0 +1,51 @@ +import { MultilineText } from './MultilineText'; + +export function Text(props: { + x: number; + y: number; + label: string; + horizontalPosition: 'left' | 'center' | 'right'; + verticalPosition: 'top' | 'center' | 'bottom'; +}) { + const { x, y, label, verticalPosition, horizontalPosition } = props; + const transform = `translate(${x},${y})`; + + let textAnchor; + switch (horizontalPosition) { + case 'left': + textAnchor = 'start'; + break; + case 'center': + textAnchor = 'middle'; + break; + case 'right': + textAnchor = 'end'; + break; + default: + textAnchor = 'middle'; + } + + return ( + <> + + + + + + + + ); +} diff --git a/src/data/getArrows.tsx b/src/data/getArrows.tsx index 783594b..0f35121 100644 --- a/src/data/getArrows.tsx +++ b/src/data/getArrows.tsx @@ -8,7 +8,7 @@ export function getArrows(nodes, options = {}) { } function getArrowsSS(nodes, arrows, status, options: any = {}) { - const { getLabel, labelPosition, getID } = options; + const { getLabel, horizontalPosition, getID, verticalPosition } = options; for (const node of nodes) { if (node.children) { @@ -20,7 +20,8 @@ function getArrowsSS(nodes, arrows, status, options: any = {}) { from={node.anchor.right} to={child.anchor.left} label={getLabel?.(child)} - labelPosition={labelPosition} + horizontalPosition={horizontalPosition} + verticalPosition={verticalPosition} />, ); } diff --git a/test/index.html b/test/index.html index a458644..360914c 100644 --- a/test/index.html +++ b/test/index.html @@ -1,69 +1,71 @@ - - - - -
-
-
- + - const responseTaxonomies = await fetch('data/taxonomies.json'); - const dataTaxonomies = await responseTaxonomies.json(); - const svgTaxonomies = TreeSVG.render(dataTaxonomies, { - nodeRenderer: 'taxonomy', - rankDepthOptions: { - maxRankDepth: 8, - }, - positionOptions: { - spacingHorizontal: 100, - }, - }); - const responseTrees = await fetch('data/trees.json'); - const dataTrees = await responseTrees.json(); - const svgTrees = TreeSVG.render(dataTrees, { - nodeRenderer: 'trees', - nodeRendererOptions: { - masses: [ - 58.065, 105.0697, 133.0647, 135.0439, 163.0752, 194.1173, 530.15, - ], - precision: 50, - numberFormat: '0.0000', - }, - positionOptions: { - spacingHorizontal: 150, - }, - }); - document.getElementById('react-tree-svg').innerHTML = svg; - document.getElementById('taxonomies-graph').innerHTML = svgTaxonomies; - document.getElementById('trees-graph').innerHTML = svgTrees; - } - getMolecules(); - function handleClick(event) { - const target = event.target; - console.log(target); - if (target.tagName === 'text') { - const url = target.getAttribute('data-url'); - if (url) { - window.open(url, '_blank'); - } + +
+
+
+ - - + } + + + + \ No newline at end of file