Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ export default App;
| `offset` | `number` | Optional number for space between element and start/end of stroke
| `svgContainerStyle` | `Style` | Style of the SVG container element. Useful if you want to add a z-index to your SVG container to draw the arrows under your elements, for example.
| `children` | `React.Node` |
| `startShape` | `Object` | An object containing the props to configure the "start shape" of the arrow. Can be one of `arrow` (default) or `circle`. See [`ShapeType`](flow-typed/archer-types.js) for a complete list of available options.
| `endShape` | `Object` | An object containing the props to configure the "end shape" of the arrow. Can be one of `arrow` (default) or `circle`. See [`ShapeType`](flow-typed/archer-types.js) for a complete list of available options.
| `startMarker` | `boolean` | Optional flag (default `false`) to also add a marker at the start of the arrow.
| `endMarker` | `boolean` | Optional flag (default `true`) to remove the marker at the end of the arrow.
| `enableStartMarker` | `boolean` | Optional flag (default `false`) to also add a marker at the start of the arrow.
| `enableEndMarker` | `boolean` | Optional flag (default `true`) to remove the marker at the end of the arrow.

#### Instance methods

Expand Down Expand Up @@ -144,9 +145,10 @@ The `ArcherStyle` type has the following shape:
strokeDasharray: number,
noCurves: boolean,
lineStyle: string,
startShape: Object,
endShape: Object,
startMarker: boolean,
endMarker: boolean,
enableStartMarker: boolean,
enableEndMarker: boolean,
}
```

Expand Down
6 changes: 5 additions & 1 deletion example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SixthExample from './SixthExample';
import SeventhExample from './SeventhExample';
import EighthExample from './EighthExample';
import NinthExample from './NinthExample';
import TenthExample from './TenthExample';

const getExample = (id: number) => {
switch (id) {
Expand Down Expand Up @@ -38,6 +39,9 @@ const getExample = (id: number) => {
case 9:
return NinthExample;

case 10:
return TenthExample;

default:
return SecondExample;
}
Expand All @@ -54,7 +58,7 @@ const App = () => {
<div>
<h2>Example {exampleId}</h2>
<p>Choose an example:</p>
{[...Array(9).keys()].map((value) => (
{[...Array(10).keys()].map((value) => (
<button
key={value}
onClick={() => setExampleId(value + 1)}
Expand Down
2 changes: 1 addition & 1 deletion example/EighthExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const FirstExample = () => {
margin: '50px',
}}
>
<ArcherContainer strokeColor="red" lineStyle="straight" offset={0} startMarker>
<ArcherContainer strokeColor="red" lineStyle="straight" offset={0} enableStartMarker>
<div style={rootStyle}>
<ArcherElement
id="root"
Expand Down
4 changes: 2 additions & 2 deletions example/NinthExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const FirstExample = () => {
strokeColor="red"
lineStyle="straight"
offset={0}
startMarker
endMarker={false}
enableStartMarker
enableEndMarker={false}
>
<div style={rootStyle}>
<ArcherElement
Expand Down
101 changes: 101 additions & 0 deletions example/TenthExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from 'react';
import ArcherContainer from '../src/ArcherContainer/ArcherContainer';
import ArcherElement from '../src/ArcherElement/ArcherElement';
const rootStyle = {
display: 'flex',
justifyContent: 'center',
};
const rowStyle = {
margin: '200px 0',
display: 'flex',
justifyContent: 'space-between',
};
const boxStyle = {
padding: '10px',
border: '1px solid black',
};

const TenthExample = () => {
return (
<div
style={{
height: '500px',
margin: '50px',
}}
>
<ArcherContainer strokeColor="red" lineStyle="straight" offset={0} enableStartMarker>
<div style={rootStyle}>
<ArcherElement
id="root"
relations={[
{
targetId: 'element2',
targetAnchor: 'top',
sourceAnchor: 'bottom',
style: {
strokeDasharray: '5,5',
startShape: {
circle: {
radius: 3,
},
},
},
},
]}
>
<div style={boxStyle}>Root</div>
</ArcherElement>
</div>

<div style={rowStyle}>
<ArcherElement
id="element2"
relations={[
{
targetId: 'element3',
targetAnchor: 'left',
sourceAnchor: 'right',
style: {
strokeColor: 'blue',
strokeWidth: 1,
enableStartMarker: false,
},
label: (
<div
style={{
marginTop: '-20px',
}}
>
Arrow 2
</div>
),
},
]}
>
<div style={boxStyle}>Element 2</div>
</ArcherElement>

<ArcherElement id="element3">
<div style={boxStyle}>Element 3</div>
</ArcherElement>

<ArcherElement
id="element4"
relations={[
{
targetId: 'root',
targetAnchor: 'right',
sourceAnchor: 'left',
label: 'Arrow 3',
},
]}
>
<div style={boxStyle}>Element 4</div>
</ArcherElement>
</div>
</ArcherContainer>
</div>
);
};

export default TenthExample;
2 changes: 1 addition & 1 deletion src/ArcherContainer/ArcherContainer.constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const endShapeDefaultProp = {
export const shapeDefaultProp = {
arrow: {
arrowLength: 10,
arrowThickness: 6,
Expand Down
35 changes: 21 additions & 14 deletions src/ArcherContainer/ArcherContainer.helpers.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import {
ValidShapeTypes,
EntityRelationType,
LineType,
SourceToTargetType,
ShapeType,
EntityRelationType,
SourceToTargetType,
ValidShapeTypes,
} from '../types';
import { SourceToTargetsArrayType } from './ArcherContainer.types';

const possibleShapes: Array<ValidShapeTypes> = ['arrow', 'circle'];

export const getEndShapeFromStyle = (shapeObj: LineType) => {
if (!shapeObj.endShape) {
export const getShapeStyleFromShape = (style: LineType, isStartShape: boolean) => {
return isStartShape ? style.startShape : style.endShape;
};

export const getShapeFromStyle = (shapeObj: LineType, isStartShape: boolean) => {
const shapeStyle = getShapeStyleFromShape(shapeObj, isStartShape);
if (!shapeStyle) {
return possibleShapes[0];
}

return (
(Object.keys(shapeObj.endShape) as ValidShapeTypes[]).filter((key) =>
(Object.keys(shapeStyle) as ValidShapeTypes[]).filter((key) =>
possibleShapes.includes(key),
)[0] || possibleShapes[0]
);
Expand All @@ -34,23 +39,24 @@ export const getSourceToTargets = (
.sort((a, b) => a.order - b.order);
};

export const createShapeObj = (style: LineType, endShape: ShapeType) => {
const chosenEndShape = getEndShapeFromStyle(style);
export const createShapeObj = (style: LineType, shape: ShapeType, isStartShape: boolean) => {
const chosenShape = getShapeFromStyle(style, isStartShape);
const shapeStyle = getShapeStyleFromShape(style, isStartShape);
const shapeObjMap = {
arrow: () => ({
arrow: {
...endShape?.arrow,
...style.endShape?.arrow,
...shape?.arrow,
...shapeStyle?.arrow,
},
}),
circle: () => ({
circle: {
...endShape?.circle,
...style.endShape?.circle,
...shape?.circle,
...shapeStyle?.circle,
},
}),
};
return shapeObjMap[chosenEndShape]();
return shapeObjMap[chosenShape]();
};

/** Generates an id for an arrow marker
Expand All @@ -61,6 +67,7 @@ export const getMarkerId = (
uniqueId: string,
source: EntityRelationType,
target: EntityRelationType,
isStartShape: boolean,
): string => {
return `${uniqueId}${source.id}${target.id}`;
return `${uniqueId}${source.id}${target.id}-${isStartShape ? 'start' : 'end'}`;
};
25 changes: 15 additions & 10 deletions src/ArcherContainer/ArcherContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React, { useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';
import ResizeObserver from 'resize-observer-polyfill';
import { SourceToTargetType } from '../types';
import { shapeDefaultProp } from './ArcherContainer.constants';
import { ArcherContainerContext, ArcherContainerContextProvider } from './ArcherContainer.context';
import { useObserveElements, useResizeListener } from './ArcherContainer.hooks';
import {
SourceToTargetsArrayType,
ArcherContainerProps,
ArcherContainerHandle,
ArcherContainerProps,
SourceToTargetsArrayType,
} from './ArcherContainer.types';
import { SvgArrows } from './components/SvgArrows';
import { endShapeDefaultProp } from './ArcherContainer.constants';
import { ArrowMarkers } from './components/Markers';
import { useObserveElements, useResizeListener } from './ArcherContainer.hooks';
import { SvgArrows } from './components/SvgArrows';

const defaultSvgContainerStyle = {
position: 'absolute',
Expand All @@ -24,17 +24,18 @@ const defaultSvgContainerStyle = {
const ArcherContainer = React.forwardRef<ArcherContainerHandle, ArcherContainerProps>(
(
{
endShape = endShapeDefaultProp,
startShape = shapeDefaultProp,
endShape = shapeDefaultProp,
strokeColor = '#f00',
strokeWidth = 2,
svgContainerStyle = {},
enableStartMarker = false,
enableEndMarker = true,
noCurves,
children,
className,
endMarker,
lineStyle,
offset,
startMarker,
strokeDasharray,
style,
}: ArcherContainerProps,
Expand Down Expand Up @@ -145,15 +146,19 @@ const ArcherContainer = React.forwardRef<ArcherContainerHandle, ArcherContainerP
<svg style={_svgContainerStyle}>
<defs>
<ArrowMarkers
enableStartMarker={enableStartMarker}
enableEndMarker={enableEndMarker}
startShape={startShape}
endShape={endShape}
sourceToTargetsMap={sourceToTargetsMap}
strokeColor={strokeColor}
uniqueId={uniqueId}
/>
</defs>
<SvgArrows
startMarker={startMarker}
endMarker={endMarker}
enableStartMarker={enableStartMarker}
enableEndMarker={enableEndMarker}
startShape={startShape}
endShape={endShape}
strokeColor={strokeColor}
strokeWidth={strokeWidth}
Expand Down
15 changes: 10 additions & 5 deletions src/ArcherContainer/ArcherContainer.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ShapeType, ValidLineStyles, SourceToTargetType } from '../types';
import { ShapeType, SourceToTargetType, ValidLineStyles } from '../types';
import { ArcherContainerContextType } from './ArcherContainer.context';

type FunctionChild = (context: React.Context<ArcherContainerContextType | null>) => React.ReactNode;
Expand Down Expand Up @@ -36,20 +36,25 @@ export type ArcherContainerProps = {
*/
offset?: number;

/**
* Customize the start shape of the line. Defaults to a traditional "arrow" (triangle) shape.
*/
startShape?: ShapeType;

/**
* Customize the end shape of the line. Defaults to a traditional "arrow" (triangle) shape.
*/
endShape?: ShapeType;

/**
* Set this to true of you want to render a marker at the start of the line
* Set this to true if you want to render a marker at the start of the line
*/
startMarker?: boolean;
enableStartMarker?: boolean;

/**
* Set this to false of you do not want to render a marker at the end of the line
* Set this to true if you want to render a marker at the end of the line
*/
endMarker?: boolean;
enableEndMarker?: boolean;

/**
* Define how the line is drawn, grid for angles, straight for direct line and curve for curves
Expand Down
Loading