Skip to content

Commit

Permalink
feat: arrow label can be aligned left and middle
Browse files Browse the repository at this point in the history
  • Loading branch information
lpatiny committed Oct 11, 2024
1 parent 8b7ab12 commit 3a647e4
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 134 deletions.
5 changes: 3 additions & 2 deletions demo/Fragmentation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: () => {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/__snapshots__/molecule.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -685,5 +685,5 @@ exports[`render: Molecule 1`] = `
<circle id="mol13:Atom:11" class="event" cx="39.96" cy="51" r="8" opacity="0"></circle>
<circle id="mol13:Atom:12" class="event" cx="60.75" cy="87" r="8" opacity="0"></circle>
<circle id="mol13:Atom:13" class="event" cx="19.18" cy="63" r="8" opacity="0"></circle>
</svg></g></g></g></g><g><path d="M 227 418 C 302 418 302 154 357 154 Q 357 154 377 154 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(302,286)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(302,286)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 227 418 C 302 418 302 432 357 432 Q 357 432 377 432 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(302,425)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(302,425)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 227 418 C 302 418 302 640 357 640 Q 357 640 377 640 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(302,529)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(302,529)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 227 418 C 302 418 302 773 357 773 Q 357 773 377 773 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(302,595.5)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(302,595.5)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 563 154 C 638 154 638 77 693 77 Q 693 77 713 77 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(638,115.5)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(638,115.5)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 563 154 C 638 154 638 224 693 224 Q 693 224 713 224 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(638,189)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(638,189)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 899 77 C 974 77 974 77 1029 77 Q 1029 77 1049 77 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(974,77)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(974,77)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 563 432 C 638 432 638 371 693 371 Q 693 371 713 371 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(638,401.5)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(638,401.5)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 563 432 C 638 432 638 502 693 502 Q 693 502 713 502 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(638,467)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(638,467)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 899 371 C 974 371 974 369 1029 369 Q 1029 369 1049 369 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(974,370)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(974,370)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g><g><path d="M 557 640 C 632 640 632 633 687 633 Q 687 633 707 633 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(632,636.5)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" alignment-baseline="middle" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(632,636.5)" stroke="none" alignment-baseline="middle" font-size="14" fill="black"></text></g></svg>"
</svg></g></g></g></g><g><path d="M 227 418 C 302 418 302 154 357 154 Q 357 154 377 154 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(302,286)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(302,286)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 227 418 C 302 418 302 432 357 432 Q 357 432 377 432 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(302,425)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(302,425)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 227 418 C 302 418 302 640 357 640 Q 357 640 377 640 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(302,529)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(302,529)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 227 418 C 302 418 302 773 357 773 Q 357 773 377 773 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(302,595.5)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(302,595.5)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 563 154 C 638 154 638 77 693 77 Q 693 77 713 77 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(638,115.5)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(638,115.5)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 563 154 C 638 154 638 224 693 224 Q 693 224 713 224 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(638,189)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(638,189)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 899 77 C 974 77 974 77 1029 77 Q 1029 77 1049 77 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(974,77)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(974,77)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 563 432 C 638 432 638 371 693 371 Q 693 371 713 371 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(638,401.5)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(638,401.5)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 563 432 C 638 432 638 502 693 502 Q 693 502 713 502 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(638,467)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(638,467)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 899 371 C 974 371 974 369 1029 369 Q 1029 369 1049 369 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(974,370)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(974,370)" stroke="none" font-size="14" fill="black"></text></g><g><path d="M 557 640 C 632 640 632 633 687 633 Q 687 633 707 633 " style="fill: none; stroke-width: 2; stroke: black; marker-end: url(#arrowhead);"></path><text text-anchor="middle" transform="translate(632,636.5)" stroke="rgba(255,255,255,0.5)" stroke-width="0.5em" fill="none" font-size="14"></text><text text-anchor="middle" transform="translate(632,636.5)" stroke="none" font-size="14" fill="black"></text></g></svg>"
`;
2 changes: 1 addition & 1 deletion src/__tests__/molecule.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ test('render: Molecule', () => {
getLabel: (node) => {
return node.transformation;
},
labelPosition: 'center',
horizontalPosition: 'center',
},
positionOptions: {
spacingHorizontal: 150,
Expand Down
44 changes: 36 additions & 8 deletions src/components/Arrow.tsx
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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 = {
Expand All @@ -37,11 +44,32 @@ export function Arrow(props: {
markerEnd: 'url(#arrowhead)',
}}
/>
{labelPosition === 'center' && (
<CenteredText x={middle.x} y={middle.y} label={label} />
{horizontalPosition === 'center' && (
<Text
x={middle.x}
y={middle.y}
label={label}
horizontalPosition={horizontalPosition}
verticalPosition={verticalPosition}
/>
)}
{labelPosition === 'right' && (
<RightText x={to.x - 12} y={to.y - 8} label={label} />
{horizontalPosition === 'right' && (
<Text
x={to.x - 12}
y={to.y - 8}
label={label}
horizontalPosition="right"
verticalPosition={verticalPosition}
/>
)}
{horizontalPosition === 'left' && (
<Text
x={from.x + 12}
y={from.y + 8}
label={label}
horizontalPosition="left"
verticalPosition={verticalPosition}
/>
)}
</g>
);
Expand Down
29 changes: 0 additions & 29 deletions src/components/CenteredText.tsx

This file was deleted.

23 changes: 23 additions & 0 deletions src/components/MultilineText.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<tspan key={index} x="0" dy={index === 0 ? `${firstDX}em` : '1em'}>

Check warning on line 17 in src/components/MultilineText.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Do not use Array index in keys
{line}
</tspan>
))}
</>
);
}
27 changes: 0 additions & 27 deletions src/components/RightText.tsx

This file was deleted.

51 changes: 51 additions & 0 deletions src/components/Text.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<text
textAnchor={textAnchor}
transform={transform}
stroke="rgba(255,255,255,0.5)"
strokeWidth="0.5em"
fill="none"
fontSize="14"
>
<MultilineText label={label} verticalPosition={verticalPosition} />
</text>
<text
textAnchor={textAnchor}
transform={transform}
stroke="none"
fontSize="14"
fill="black"
>
<MultilineText label={label} verticalPosition={verticalPosition} />
</text>
</>
);
}
5 changes: 3 additions & 2 deletions src/data/getArrows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function getArrows(nodes, options = {}) {
}

function getArrowsSS(nodes, arrows, status, options: any = {}) {

Check warning on line 10 in src/data/getArrows.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Unexpected any. Specify a different type
const { getLabel, labelPosition, getID } = options;
const { getLabel, horizontalPosition, getID, verticalPosition } = options;

for (const node of nodes) {
if (node.children) {
Expand All @@ -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}
/>,
);
}
Expand Down
Loading

0 comments on commit 3a647e4

Please sign in to comment.