pierpo / react-archer Goto Github PK
View Code? Open in Web Editor NEW๐น Draw arrows between React elements ๐
Home Page: https://pierpo.github.io/react-archer/
License: MIT License
๐น Draw arrows between React elements ๐
Home Page: https://pierpo.github.io/react-archer/
License: MIT License
This library is proving very useful to me, so thanks for building it! I'm having a problem where the SVGs are being duplicated when new elements are added to the DOM. I'm using react-dnd to drag an element onto a target. When that happens, a new object is added to an array in state, and the DOM updates accordingly. But when this happens, the existing SVGs are duplicated, as well as a new SVG being added. It's impossible to see visually, but it makes react throw lots of 'two children with the same key' errors. At the moment I can work around it by momentarily returning an empty div in render().
Is there a better way to trigger a redraw?
The library draws lines just fine for on screen parts of the container.
But when I scroll horizontally, I see that nothing is drawn in the area of the screen that was offscreen.
In re-writing some of the internals, I discovered that the relations
attribute is a leaky abstraction, in that it can house any relations whatsoever; they don't have to be related to the ArcherElement in which they are passed.
So, I added an array API for the to
aspect of the relation without realizing relations
can draw an arrow from any node to any other node.
One step farther is removing the relations
array, inferring the from
ID as the id
for the given ArcherElement and adding a new prop called to
, which allows for an array as shown in my open PR. This simplifies the API substantially and is much more performant.
Thoughts @pierpo?
I added your library to my project where I use Jest for testing, and since then I have the following error while running test in my project:
TypeError: environment.teardown is not a function
According to this an article at Stackoverflow [1] and my investigation the Jest version which is installed with react-archer
conflicts with the version which is needed at my project. However react-archer
should not depend on jest
and jest-environment-jsdom
, because the testing framework as well as react-scrips
are dev dependencies.
Please move react-scripts
which generates conflicting dependencies to devDependencies
. This solves the problem for me.
I'm finding that arrowheads do not always display. the line is there, it just stops short. is this a known issue?
I have a component that looks a little something like:
<MyApp>
<ArcherContainer>
<ThirdPartyComponent itemRenderer={() => (<ContainsArcherElement />)}
</ArcherContainer>
</MyApp>
And then ContainsArcherElement
is similar to:
const ContainsArcherElement = () => (
<SomethingElse>
<ArcherElement /> // with all the right props
</SomethingElse>
)
But I'm getting the full Could not find "unregisterChild" in the context of <ArcherElement>. Wrap the component in a <ArcherContainer>.
but it should be wrapped, just not immediately.
Is there a way around this, and is it a bug or a feature?
Thank you for this arrow library it has been useful but I am struggling to figure out a scaling issue.
I am using react-easy-panzoom as a wrapper component around react-archer elements to be able to pan and zoom around a large flow chart. I have had similar results with different pan-zoom libraries.
At PanZoom zoom level of "1" (no zoom) I can click->drag and pan around the chart and resize the containing window and everything works fine.
The issue is that if I zoom out with the mouse wheel using the PanZoom functionality the react-archer arrows scale properly and I can pan without issue, but then after resizing the window while zoomed out the react-archer arrows seem to rerender at the wrong scale. The further I zoom out and then resize the window the smaller the arrows will scale down to.
https://codesandbox.io/s/react-easy-panzoom-svg-scaling-behavior-g8vcn?fontsize=14
<div>
<ArcherContainer strokeColor="red">
<div style={gridWrapper}>
<ArcherElement
id="source"
relations={[
{
targetId: "target",
targetAnchor: "left",
sourceAnchor: "right"
}
]}
>
<div style={boxStyle}>Root</div>
</ArcherElement>
<ArcherElement id="target" relations={[]}>
<div style={boxStyle}>Root</div>
</ArcherElement>
</div>
</ArcherContainer>
</div>
</PanZoom>
PanZoom zoom level 1 https://i.imgur.com/xr9RRQr.jpg
zoom out https://i.imgur.com/UjHUJ1m.jpg
resize after zoom out https://i.imgur.com/QcGzQWj.jpg
In trying to achieve animation of arrows on div scrolling, I ran into a couple of issues. I achieved this:
But through some scullduggery.
I extended the ArcherContainerProps interface to contain:
ref?: any
forceUpdate?: () => void
So that I could grab the reference to the archer container and also forceUpdate the component (not sure why I couldn't access these generic react props?)
Then I attached the event listeners to my divs and on scroll I forceUpdate the component.
export default class Space extends Component<Props, State> {
state = {
currentSelectionIdx: 0,
}
baseSpaceRef = React.createRef<HTMLDivElement>()
linkSpaceRef = React.createRef<HTMLDivElement>()
archerContainerRef = React.createRef<ArcherContainerProps>()
componentDidMount = () => {
this.baseSpaceRef.current &&
this.baseSpaceRef.current.addEventListener('scroll', this.isScrolling)
this.linkSpaceRef.current &&
this.linkSpaceRef.current.addEventListener('scroll', this.isScrolling)
}
componentWillUnmount = () => {
this.baseSpaceRef.current &&
this.baseSpaceRef.current.removeEventListener('scroll', this.isScrolling)
this.linkSpaceRef.current &&
this.linkSpaceRef.current.removeEventListener('scroll', this.isScrolling)
}
isScrolling = () => {
this.archerContainerRef!.current!.forceUpdate()
}
render() {
return (
<div>
<ArcherContainer ref={this.archerContainerRef}>
<div className="space">
<div className="baseSpace" ref={this.baseSpaceRef}>
{this.baseCards()}
</div>
<div className="linkedSpace" ref={this.linkSpaceRef}>
{this.linkedCards()}
</div>
</div>
</ArcherContainer>
</div>
)
}
}
It feels hacky though.
I was wondering if you could supply the typescript compliant functionality? It may not need a new feature, just I haven't found it yet.
see here(the official example with a little change): https://codesandbox.io/s/react-archer-uses-wlqxv
position: absolute not working well, and there is no option to offset arrow anchors by given property.
any help?
I noticed that updating the relations
prop doesn't actually trigger any updates in componentWillReceiveProps
in the ArcherElement
component because of the relationsWithStringifiedLabels
equality check that uses the label
on the relations... so not sure how exactly that should be fixed right now, I suppose it should just check if there is any relation added or removed and then run the appropriate update?
Hello all!
I'm just starting to work with this library and really excited about it! I'm testing it right now and I'm trying to make it work with a Grid CSS Layout. Unfortunately I'm having some trouble here. I uploaded my project at Code Sandbox: https://codesandbox.io/s/4tx93
If you open the screen outside of editor or resizes it, you gonna see what I'm talking about, the arrows seems to be coming from the center of column instead of the elements directly. Another problem that I see, is that the arrows are overlapping each element, going over the elements. If I add justify-self: center;
to the .col members, it works, but I don't want it actually.
Thanks.
When there are > 4 paths, labels get stuck under the path on the right and become unclickable.
Code sandbox with reproduction:
https://codesandbox.io/s/magical-hypatia-hgyoc?fontsize=14
This is copied from example #2 with added click events.
As you can see, the label "3 hello" is unclickable.
Possible to add a new anchor location, which is directly to the middle of the source/target element?
Would it be as simple as adding an additional case to the switch in ArcherContainer.js L55
(https://github.com/pierpo/react-archer/blob/master/src/ArcherContainer.js#L55)...
Something like return rectToPoint(rect).add(new Point(rect.width / 2, rect.height / 2))
?
Give the provision to link multiple nodes from single node
Hello,
First, thanks for the package it fit great with the import CSV system I'm trying to code (in the part where the users have to "link" them web table columns with the CSV's).
Here is how i used the package:
-From the parent View, I'm mapping companyFields from states and columnsRelation from props:
-From the LinkableAttribute component :
The issue :
Arrows are created nicely, but when i try to update the columnsRelations, components are both seems like updating in console but the arrows doesn't "re-render", i tried to forceUpdate when i call my Redux Action to dispatch changes, i tried to set shouldComponentUpdate etc.
Here is a GIF of the issue, i first create arrows, then try to update ... the updates are shown only on manual page refresh.
Thanks in advance
Thanks for the awesome package @pierpo! Is there any way we can set the opacity of an ArcherElement
? I know that strokes have a stroke-opacity
css property but adding that to an ArcherElement
's style
doesn't seem to do anything.
Tested this library inside a container with draggable elements and it seems that the ArcherContainer
element adds a div with the ref storeParent
around the child elements, right after the SVG. Because this collapses its height up to the position of the lowest element it's not really useful for this use case, I fixed it by adding the same styles as the svgContainerStyle
, notably 100% height & width...
Would it be possible to update this CSS or allow passing a custom style prop to adjust this?
Not even sure if there needs to be a separate div element to render the child elements.
I can submit a PR if you want.
With version 0.3.0, the marker elements use a random key as id/key, but it became hard to test using snapshots. Does anyone have an approach to test it?
react-archer/src/ArcherContainer.js
Lines 287 to 288 in 5eb6b3a
react-archer/src/ArcherContainer.js
Lines 105 to 109 in 5eb6b3a
Hi, first of all, thanks for this beautiful and simple library!
I'm working on a tool to visualize relationships in text, I would like to display arrows alongside the text, the text can be disposed vertically or horizontally.
in the vertical scenario what I'm getting now is this
and this is a portion of the relationship object:
targetAnchor: "left", sourceAnchor: "left", style: { strokeColor: "#c0c0c0", strokeWidth: 2, strokeDasharray: 2, arrowLength: 0, arrowThickness: 0, noCurves: true, },
I don't understand why this is happening.
Just gave a quick glimpse at the source code, and would be happy to purpose a pull request if you can give me some advice!
Thanks!
P.S. It would be also nice to have the possibility of not drawing the pointer, and instead draw just a line
If the elements within the container ever change position, the arrows do not seem to change their position to match
Every now and then the ArcherContainer fails to unobserve properly. This is happening maybe every third time I render my component
Hey @pierpo - Nice work on the project. We want to use it, but I think we'll need a couple things.
The first is a way to pass a list of ids (array, string format, etc.) for to
relations to paint multiple arrows from one DOM element. Also, we'll need to use an initialize script similar to react-dates
for adding event listeners to window
so that we can use this with a universal rendering setup. I personally don't want to delve into writing server rendering logic for this.
Would you be interested in upstreaming these features or do you have any opinions for how to implement them? I would understand not wanting to upstream the initialize script especially. I dislike the semantics but we need it to use the library correctly.
Here is a sandbox link to replicate the issue.
https://codesandbox.io/s/musing-beaver-bzk9y
As I have understood after reading the documentation the placement of the arrows is ambiguous and I think it is a bug.
On changing the targetId: "element4" again places arrow wrongly.
Thanks
Hey, if remove an element from the start of array I have this issue. If I remove from the end of the array - I don't have this problem. Maybe whoever has an idea of how to fix it
I use version 1.5
React 16.8
<ArcherContainer noCurves={true} strokeColor={colors.blue500} strokeWidth={1} arrowThickness={7}>
{....}// here i have array of cards
</>
To unregister transitions when an ArcherElement unmounts, it should delete all the svg's with both to
and from
this element. So this would be unregisterAllTransitions
on the context.
And also, the ref to the element is still there in the container state so the ArcherElement should also have unregisterChild
and also call this on unmount.
What are your thoughts about this @pierpo ? I can submit a PR (working on it at the moment, seems to work for my use case, dynamically adding and removing elements and drawing arrows between them, was sometimes using a temporary element for dragging with the same id & ref so needed to fix this).
Thanks for this library @pierpo , it's very nice.
The way element relations work, all relations to a source element must be known at the time the source element is rendered. Sometimes it would be programmatically convenient to express all relations to a target element instead of a source element. We could accomplish this by adding a single flag to the arrow style that indicates that instead of being drawn forward, with the arrowhead pointing at the target, the arrow should be draw backward, with the arrowhead pointing at the source.
Style
{
strokeColor: string,
strokeWidth: number,
strokeDasharray: number,
arrowLength: number,
arrowThickness: number,
noCurves: boolean,
arrowForward?: boolean // New property. Defaults to True.
}
Then in generateAllArrowMarkers
draw the arrowhead pointing at the source if the arrowForward
property is False
.
Hey @pierpo,
We've deployed to our staging environment code that uses react-archer for the first time and are experiencing the following (breaking) error:
Uncaught TypeError: Super expression must either be null or a function
at 3.eaf63c57.chunk.js:412
...
...
(the error is not present while working locally)
After extensive investigation, we were able to narrow down the issue to webpack minification, i.e. the error no longer appears (and the web app runs properly) when we add minimize: false
under optimization
(in webpack.config.js
).
We don't want to disable minification globally, so now we are trying to disable it only on the code that uses react-archer.
Are you familiar with this minification issue? Relevant solutions?
We're using react 16.9.0
with webpack 4.3.2
.
Thanks,
Dima.
I am working on a tree view structure. When I remove one of the nodes from my tree (wrapped in an archer element) I get the error: Uncaught TypeError: Failed to execute 'unobserve' on 'ResizeObserver': parameter 1 is not of type 'Element'.
from React Archer.
I'm not sure if you have seen this issue before, but removing the archer container and archer elements from my project works as expected.
Thanks for making this repo. I wanted to try it out but ran into an issue using 0.2.3.
For some reason, I can't require version 0.2.3. I've checked the diffs and I can't figure out what happened since 0.2.2 that would have broken it.
yarn init
yarn add [email protected]
node
require("react-archer")
Error: Cannot find module 'react-archer'
at Function.Module._resolveFilename (module.js:513:15)
at Function.Module._load (module.js:463:25)
at Module.require (module.js:556:17)
at require (internal/module.js:11:18)
at repl:1:1
at ContextifyScript.Script.runInThisContext (vm.js:50:33)
at REPLServer.defaultEval (repl.js:240:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval] (domain.js:314:12)
at REPLServer.onLine (repl.js:441:10)
0.2.2 works fine
yarn init
yarn add [email protected]
node
require("react-archer")
{ ArcherElement: [Getter], ArcherContainer: [Getter] }
Do you have any idea what is going on?
Firstly, thanks a lot for such a useful library! This is of really great help :)
Coming to my requirement, I was building a flowchart/concept-map kind of thing and need some of the nodes to be editable. I tried to put <input>
inside <ArcherElement>
, but it didn't work because of the SVG overlay. I tried putting <input>
inside <foreignObject>
, but it didn't help either.
Is there any way to put form elements/content editable DIVs inside ArcherElements?
this would make it possible to ensure it's above the elements it is drawing arrows between.
First of all, great library, thank you for the work @pierpo. I'm getting a weird error which reason I can't figure out. The elements are connected perfectly with the arrows during development, but when I build the app or the storybook where the component is used, the path is not the same, they seem to take a very long path instead of the shortest one.
@pierpo Have you seen this happening before? If not, I can try to make a reproduction.
In the last few days, I've spent a little time trying to make my application compatible with the latest changes in react archer. I've noticed a few things that could be made better and easier. I took the components as they are in the latest version of react-archer and integrated them directly into my project. For more information, see the following ticket or pull request in my application.
Issue description
project-millipede/millipede-docs#202
Commit
project-millipede/millipede-docs@ac8fd2b
Link to a demo
https://millipede.me/perspective/strategy
Among other things, the modifications solve recent compatibility problems and breaking changes of react-archer. If the authors of react-archer are interested, I can also provide the changes as pull requests in react archer. Of course, this requires some further adjustments in the build process of the library, where the sources got entirely migrated to typescript.
Please tell me what you think, feasible or not.
Thx!
Nice library. I'm trying to get an arrow to connect the tops of two adjacent elements:
const relations = [{
targetId: "element" + (i+1),
sourceAnchor: "top",
targetAnchor: "top"
}]
But it seems to misunderstand where "top" is on the target:
I'd like for the line to come out the top and curve back down into the top of the target. Weirdly it works if I actually put the elements below each other:
But if I then try to go from the right->top or bottom->top, it still breaks:
It seems the library is calculating arrowhead direction based on the vertical offset of start and end points, when it should be instead dictated by the anchor itself (a "top" anchor should always have an arrowhead pointing down into it at the top)
Full code at: https://codesandbox.io/s/dynamic-number-and-ticker-and-lookatmouseelement-jy8uv , scroll down in the page and look at Arrows.tsx
There is a use case I have where I would like the lines drawn stepped.
Currently:
I currently have:
const CONTAINER_STYLE = {
arrowLength: 0,
noCurves: true,
offset: 0,
strokeColor: "#50616f",
};
and:
const ANCHOR_SETTINGS: IAnchorPositions = {
sourceAnchor: "bottom",
targetAnchor: "left",
};
Is there anything I can do currently to achieve my desired result? Thank you!
Related to #39. It would be great if it where possible to customize the beginning/start/origin of the arrow. E.g. it could be added a circle at the beginning to mark the attachment of the arrow at some object:
Hi There! Great library - it's become very useful!
I created two different containers so I could have two different colored arrows, however, it appears the arrow heads share the color of the first one defined. The strokeColor
seems to properly control the the color of the line, but the arrow heads are the same color.
I did see that individually colored arrows are on the TODO list so I don't know if this is a known issue or not. I noticed that the html id
field is arrow
for all arrow head marker objects, so there may a conflict occurring there.
Thanks!
I've seen people needing something else than an arrow at the extremity.
Would be nice to be able to customize the extremities to have a dot instead of a triangle, for example.
So I would like to request a feature, making the SVG arrows clickable (and selectable), for which I have already been trying to add some code but I seem to have some trouble to get it working.
Some required adjustments include:
cursor
which can be pointer
like in CSS (can also be set on the SVG element, preferably <g>
)pointerEvents
on the <g>
element, set to "all"
or "stroke"
or similar. This might even be a default without needing a custom prop passed to ArcherContainer
onClick
handler can be passed through props as well to handle the click event on the arrow, which should be added to the <path>
element itself.The only problem right now is that this works outside of the library but not when I adjust the actual code in SvgArrow
so something else seems to prevent the pointer event from working:
<g cursor="pointer" pointerEvents="all">
<path onClick={clickHandler} ... />
</g>
I'm not sure how challenging this would be with your svg implementation, but it would be a welcome addition!
Hello,
Is it currently possible to add svg background overlays?
Thanks
Minified bundle used for production causes arrows not to display as pretty curves
These arrows look great when in dev mode. But once I build a prod (using npm build) to go to production, the arrows lose their curve and therefore look straight & warped :(
Hi there,
First of all thanks to @pierpo for the great library, everything has been extremely easy to integrate and use and has been super useful so far.
The issue I am having at the moment is when trying to programatically control the zoom of my ArcherContainer
using a transform(zoom)
CSS method the arrows are not being re-rendered correctly. Everything else within the container scales as desired however the arrows now no longer point to the desired target as well as they previously did.
I have put together a simple CodeSandbox demonstrating this here:
https://codesandbox.io/s/quirky-minsky-5ou2h?file=/src/App.js
Similarly, I have attempted to use a ref
and the ref.current.refreshScreen()
method which is exposed which also did not work for me.
Many thanks for any help! ๐
The sample source code is as follows:
import React, { useState, useEffect, useRef } from "react";
import "./styles.css";
import { ArcherContainer, ArcherElement } from "react-archer";
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 App = () => {
const [zoom, setZoom] = useState(1.0);
let archerContainerRef = useRef(null);
function _incrementZoom() {
setZoom(prevState => {
return (prevState += 0.1);
});
archerContainerRef.current.refreshScreen();
}
function _decrementZoom() {
setZoom(prevState => {
return (prevState -= 0.1);
});
archerContainerRef.current.refreshScreen();
}
return (
<div>
<div style={{ marginBottom: 10 }}>Current Zoom: {zoom}</div>
<div
style={{ backgroundColor: "green", width: 180, marginBottom: 10 }}
onClick={() => _incrementZoom()}
>
Increment Zoom (+)
</div>
<div
style={{ backgroundColor: "red", width: 180, marginBottom: 10 }}
onClick={() => _decrementZoom()}
>
Decrement Zoom (-)
</div>
<ArcherContainer
strokeColor="red"
ref={archerContainerRef}
style={{ transform: `scale(${zoom})` }}
>
<div style={rootStyle}>
<ArcherElement
id="root"
relations={[
{
targetId: "element2",
targetAnchor: "top",
sourceAnchor: "bottom"
}
]}
>
<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 },
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 App;
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.