Bug:Programatically changing connections object in Anchor does not update the connection #305
Replies: 8 comments 2 replies
-
Here's a minimum reproducible example <script lang="ts">
import { Node, Svelvet, Controls } from "svelvet";
let connections = ["gamma"];
</script>
<div class="h-screen w-full">
<Svelvet id="my-canvas" TD minimap fitView>
<Node
id="beta"
bgColor="green"
label="Beta"
bind:connections
on:nodeReleased={() => {
console.log("switching connections");
connections = [connections[0] === "gamma" ? "alpha" : "gamma"];
}}
/>
<Node
id="alpha"
bgColor="red"
label="Alpha"
position={{ x: 250, y: 100 }}
/>
<Node
id="gamma"
bgColor="blue"
label="Gamma"
position={{ x: 250, y: -100 }}
inputs={1}
/>
<Controls />
</Svelvet>
</div> |
Beta Was this translation helpful? Give feedback.
-
similar issue with anchors, but with anchors the connections is coming out to be null when i try to switch? <script lang="ts">
import { Node, Svelvet, Anchor } from "svelvet";
let connections: [string, string][] = [["gamma", "gamma-anchor"]];
</script>
<div class="h-screen w-full">
<Svelvet id="my-canvas" TD minimap fitView>
<Node
id="beta"
bgColor="green"
label="Beta"
on:nodeReleased={() => {
console.log("switching connections", connections);
if (!connections || !connections[0]) return;
connections = [
connections[0][0] === "gamma"
? ["alpha", "alpha-anchor"]
: ["gamma", "gamma-anchor"],
];
}}
>
<div class="w-40 h-20" />
<Anchor id="beta-anchor" {connections} />
</Node>
<Node id="alpha" bgColor="red" label="Alpha" position={{ x: 250, y: 100 }}>
<div class="w-40 h-20" />
<Anchor id="alpha-anchor" />
</Node>
<Node
id="gamma"
bgColor="blue"
label="Gamma"
position={{ x: 250, y: -100 }}
inputs={1}
>
<div class="w-40 h-20" />
<Anchor id="gamma-anchor" />
</Node>
</Svelvet>
</div> |
Beta Was this translation helpful? Give feedback.
-
This is actually more of a feature request. The connections prop is just for specifying connections prior to runtime. We process this after all the Nodes have mounted and don't react to changes. It's assumed, perhaps incorrectly, that dynamic connections are made using the click/drag behavior of the UI. Not at all against enabling this, but it will definitely require some thought to do it elegantly. |
Beta Was this translation helpful? Give feedback.
-
Still toying with different ideas, but I think I've figured out a pretty cool way to do this. I think the best way to enable this is not through the connections array, but through let:connect and let:disconnect functions on the Node and Anchor. I can maybe make it a global function as well that you could call from anywhere, rather than just the context of your component. Not sure yet. I think we just need to be clearer that the connections prop is not bound. The declarative approach makes sense prior to runtime, but I don't think it's at all intuitive to "redeclare" things programmatically in this case. You can easily push connections into that array even if we've changed it behind the scenes, but figuring out which ones to delete to get what you want is complicated considering that it doesn't represent the actual connections. It's just a shorthand and may not capture which anchor the connection is made from or to. How we would even push connections made from the UI (which feature more precise descriptions) back into the connections array in a way that makes sense is not clear to me. It's absolutely possible and I'll put some thought into it, but I think an imperative approach makes more sense in the short term. If you want to disconnect from "gamma" and connect to "alpha", just call those functions. We could enable the same simplicity of the connections array by just handling the disconnect logic under the hood in a first-in-first-out kind of way. Call Screen.Recording.2023-04-28.at.1.46.29.PM.mov<script lang="ts">
import { Svelvet, Node, Anchor } from '$lib';
</script>
<body>
<Svelvet>
<Node
position={{ x: 100, y: 300 }}
dimensions={{ width: 400, height: 300 }}
let:connect
let:disconnect
useDefaults
>
<div>
<button on:click={() => connect(2)}>Connect 2</button>
<button on:click={() => disconnect(2)}>Disconnect Last From 2</button>
<button on:click={() => disconnect(2, '3')}>Disconnect Node 2/Anchor 3</button>
<button on:click={() => connect(3)}>Connect 3</button>
</div>
<Anchor />
</Node>
<Node inputs={4} position={{ x: 600, y: 200 }} />
<Node inputs={5} position={{ x: 600, y: 600 }} />
</Svelvet>
</body>
<style>
body {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
}
</style> |
Beta Was this translation helpful? Give feedback.
-
Love this approach! But definitely need a little more thought to concretize, having these functions on a global scope would be very much helpful and enable many more use cases. One thing I always think of before implementing new APIs is that is there any downside of this approach? and any downside if we expose it globally? I dont see any right now apart from the fact that there might be a better way to do this altogher, let me study how vue flow & react flow do the same and get back. |
Beta Was this translation helpful? Give feedback.
-
For react flow, seems like things can be changed programmatically via their Instance API https://reactflow.dev/docs/api/react-flow-instance/#addedges For vue flow, looks like they have a similar api where edges are passed as an array with source & target anchors specified beforehand. https://vueflow.dev/examples/nodes/ After understanding how complicated it is in changing edges in the above two libraries, How are you planning to expose this globally tho? I guess it'll take two props then? the source & the target anchor/node id. |
Beta Was this translation helpful? Give feedback.
-
So, it's very easy to expose it globally, you just lose some of the convenience of using the method directly accessible on the Nodes/Anchors. For instance, if you used it outside of the Svelvet component context, you'd have to pass in the graph ID in addition to both Node/Anchor IDs. I can think about ways around that. It's just about what to prioritize. The idea of calling a function that has the component context already captured is the real beauty of this. The only downside to me is that it's scoped to within the component tree. What I'd really like is to somehow inject the function into the Node as a prop rather than a slot prop, so that you can easily access it in the script tag of the component. This is possible, but it would just require some refactoring. For now, the version I'll push out this weekend will just expose them via the let:directive as seen above + a global version. |
Beta Was this translation helpful? Give feedback.
-
Perfect! This should do for now, and we can plan a more sophisticated version over time :) |
Beta Was this translation helpful? Give feedback.
-
Let's say i have three nodes A, B, C
Now ideally if i modify the
connections
object & bind it toAnchor
, it should work, right?Minimum reproducible anchors shared below, for both
Node
&Anchor
Beta Was this translation helpful? Give feedback.
All reactions