first working version with using $state for nodes & edges

This commit is contained in:
peterkogo
2024-12-09 12:07:10 +01:00
parent 4fcde6f6bf
commit d0fce3984d
24 changed files with 400 additions and 366 deletions
@@ -14,11 +14,11 @@
onedgemouseleave
}: { store: SvelteFlowStore } & EdgeEvents = $props();
const { edges, nodes } = store;
// const { edges, nodes } = store;
let layoutedEdges = $derived.by(() => {
$nodes;
const layoutedEdges = $edges.reduce<EdgeLayouted[]>((res, edge) => {
store.nodes;
const layoutedEdges = store.edges.reduce<EdgeLayouted[]>((res, edge) => {
const sourceNode = store.nodeLookup.get(edge.source);
const targetNode = store.nodeLookup.get(edge.target);
@@ -2,7 +2,7 @@
import { onDestroy } from 'svelte';
import { nodeHasDimensions } from '@xyflow/system';
import type { NodeEvents } from '$lib/types';
import type { Node, NodeEvents } from '$lib/types';
import { NodeWrapper } from '$lib/components/NodeWrapper';
import type { SvelteFlowStore } from '$lib/store/types';
@@ -19,7 +19,7 @@
onnodedragstop
}: { store: SvelteFlowStore; nodeClickDistance?: number } & NodeEvents = $props();
const { nodes } = store;
// const { nodes } = store;
const resizeObserver: ResizeObserver | null =
typeof ResizeObserver === 'undefined'
@@ -43,10 +43,19 @@
onDestroy(() => {
resizeObserver?.disconnect();
});
function give(node: Node) {
const internalNode = store.nodeLookup.get(node.id);
// console.log('this reruns most definitely', internalNode);
console.log(internalNode?.position);
return internalNode;
}
// $inspect(store.nodes).with(console.trace);
</script>
<div class="svelte-flow__nodes">
{#each $nodes as node (node.id)}
{#each store.nodes as node (node.id)}
{@const internalNode = store.nodeLookup.get(node.id)}
<NodeWrapper
{store}
@@ -45,7 +45,7 @@
children
}: PaneProps = $props();
const { nodes, edges } = store;
// const { nodes, edges } = store;
// svelte-ignore non_reactive_update
let container: HTMLDivElement;
@@ -126,7 +126,7 @@
height: Math.abs(mousePos.y - startY)
};
const prevSelectedNodeIds = selectedNodes.map((n) => n.id);
const prevSelectedEdgeIds = getConnectedEdges(selectedNodes, $edges).map((e) => e.id);
const prevSelectedEdgeIds = getConnectedEdges(selectedNodes, store.edges).map((e) => e.id);
selectedNodes = getNodesInside(
store.nodeLookup,
@@ -135,7 +135,7 @@
store.selectionMode === SelectionMode.Partial,
true
);
const selectedEdgeIds = getConnectedEdges(selectedNodes, $edges).map((e) => e.id);
const selectedEdgeIds = getConnectedEdges(selectedNodes, store.edges).map((e) => e.id);
const selectedNodeIds = selectedNodes.map((n) => n.id);
// this prevents unnecessary updates while updating the selection rectangle
@@ -143,14 +143,14 @@
prevSelectedNodeIds.length !== selectedNodeIds.length ||
selectedNodeIds.some((id) => !prevSelectedNodeIds.includes(id))
) {
nodes.update((nodes) => nodes.map(toggleSelected(selectedNodeIds)));
// store.nodes.update((nodes) => nodes.map(toggleSelected(selectedNodeIds)));
}
if (
prevSelectedEdgeIds.length !== selectedEdgeIds.length ||
selectedEdgeIds.some((id) => !prevSelectedEdgeIds.includes(id))
) {
edges.update((edges) => edges.map(toggleSelected(selectedEdgeIds)));
// store.edges.update((edges) => edges.map(toggleSelected(selectedEdgeIds)));
}
store.selectionRectMode = 'user';
@@ -1,5 +1,8 @@
<!-- TODO: why is this neccessary for types to work?? -->
<svelte:options runes={true} />
<script lang="ts">
import { onMount, getContext, setContext } from 'svelte';
import { getContext, setContext, onDestroy } from 'svelte';
import cc from 'classcat';
import { PanOnScrollMode } from '@xyflow/system';
@@ -64,28 +67,45 @@
connectionLineStyle = '',
attributionPosition,
children,
nodes = $bindable([]),
edges = $bindable([]),
...props
}: SvelteFlowProps = $props();
// svelte-ignore perf_avoid_inline_class
const container: ContainerSignals = new (class {
domNode = $state<HTMLDivElement>();
width = $state<number | undefined>(width);
height = $state<number | undefined>(height);
})();
let domNode = $state<HTMLDivElement>();
let clientWidth = $state<number | undefined>(width);
let clientHeight = $state<number | undefined>(height);
const store = createStore({
props,
container
get domNode() {
return domNode;
},
get width() {
return clientWidth;
},
get height() {
return clientHeight;
},
get nodes() {
return nodes;
},
set nodes(newNodes) {
nodes = newNodes;
},
get edges() {
return edges;
},
set edges(newEdges) {
edges = newEdges;
}
});
store.syncNodeStores(props.nodes);
store.syncEdgeStores(props.edges);
const providerContext = getContext<ProviderContext>(key);
if (providerContext) {
providerContext.setStore(store);
}
setContext(key, {
provider: false,
getStore() {
@@ -93,93 +113,94 @@
}
} satisfies StoreContext);
onMount(() => {
return () => {
store.reset();
};
onDestroy(() => {
store.reset();
});
let colorModeClass = $derived(useColorModeClass(store.colorMode));
</script>
<div
bind:this={container.domNode}
bind:clientWidth={container.width}
bind:clientHeight={container.height}
style:width
style:height
{style}
class={cc(['svelte-flow', className, $colorModeClass])}
data-testid="svelte-flow__wrapper"
{...props}
role="application"
>
<KeyHandler
{store}
{selectionKey}
{deleteKey}
{panActivationKey}
{multiSelectionKey}
{zoomActivationKey}
/>
<Zoom
{store}
{onMoveStart}
{onMove}
{onMoveEnd}
{panOnScrollMode}
{preventScrolling}
{zoomOnScroll}
{zoomOnDoubleClick}
{zoomOnPinch}
{panOnScroll}
{panOnDrag}
{paneClickDistance}
<!-- TODO: this is the only hack in the library so far. Can we get rid of it? -->
{#if store.adoptNodes && store.adoptEdges}
<div
bind:this={domNode}
bind:clientWidth
bind:clientHeight
style:width
style:height
{style}
class={cc(['svelte-flow', className, $colorModeClass])}
data-testid="svelte-flow__wrapper"
{...props}
role="application"
>
<Pane {store} {onpaneclick} {onpanecontextmenu} {panOnDrag} {selectionOnDrag}>
<ViewportComponent {store}>
<EdgeRenderer
{store}
{onedgeclick}
{onedgecontextmenu}
{onedgemouseenter}
{onedgemouseleave}
/>
<ConnectionLine
{store}
containerStyle={connectionLineContainerStyle}
style={connectionLineStyle}
{connectionLine}
/>
<div class="svelte-flow__edgelabel-renderer"></div>
<div class="svelte-flow__viewport-portal"></div>
<NodeRenderer
{store}
{nodeClickDistance}
{onnodeclick}
{onnodecontextmenu}
{onnodemouseenter}
{onnodemousemove}
{onnodemouseleave}
{onnodedrag}
{onnodedragstart}
{onnodedragstop}
/>
<NodeSelection
{store}
{onselectionclick}
{onselectioncontextmenu}
{onnodedrag}
{onnodedragstart}
{onnodedragstop}
/>
</ViewportComponent>
<UserSelection />
</Pane>
</Zoom>
<Attribution {proOptions} position={attributionPosition} />
{@render children?.()}
</div>
<KeyHandler
{store}
{selectionKey}
{deleteKey}
{panActivationKey}
{multiSelectionKey}
{zoomActivationKey}
/>
<Zoom
{store}
{onMoveStart}
{onMove}
{onMoveEnd}
{panOnScrollMode}
{preventScrolling}
{zoomOnScroll}
{zoomOnDoubleClick}
{zoomOnPinch}
{panOnScroll}
{panOnDrag}
{paneClickDistance}
>
<Pane {store} {onpaneclick} {onpanecontextmenu} {panOnDrag} {selectionOnDrag}>
<ViewportComponent {store}>
<EdgeRenderer
{store}
{onedgeclick}
{onedgecontextmenu}
{onedgemouseenter}
{onedgemouseleave}
/>
<ConnectionLine
{store}
containerStyle={connectionLineContainerStyle}
style={connectionLineStyle}
{connectionLine}
/>
<div class="svelte-flow__edgelabel-renderer"></div>
<div class="svelte-flow__viewport-portal"></div>
<NodeRenderer
{store}
{nodeClickDistance}
{onnodeclick}
{onnodecontextmenu}
{onnodemouseenter}
{onnodemousemove}
{onnodemouseleave}
{onnodedrag}
{onnodedragstart}
{onnodedragstop}
/>
<NodeSelection
{store}
{onselectionclick}
{onselectioncontextmenu}
{onnodedrag}
{onnodedragstart}
{onnodedragstop}
/>
</ViewportComponent>
<UserSelection />
</Pane>
</Zoom>
<Attribution {proOptions} position={attributionPosition} />
{@render children?.()}
</div>
{/if}
<style>
.svelte-flow {
@@ -59,7 +59,7 @@ export type SvelteFlowProps = NodeEvents &
* }
* ]);
*/
nodes: Writable<Node[]>;
nodes: Node[];
/** An array of edges to render in a controlled flow.
* @example
* const edges = writable([
@@ -70,7 +70,7 @@ export type SvelteFlowProps = NodeEvents &
* }
* ]);
*/
edges: Writable<Edge[]>;
edges: Edge[];
/** Custom node types to be available in a flow.
*
* Svelte Flow matches a node's type to a component in the nodeTypes object.
@@ -191,7 +191,7 @@ export type SvelteFlowProps = NodeEvents &
/**
* Controls if nodes should be automatically selected when being dragged
*/
selectNodesOnDrag: boolean;
selectNodesOnDrag?: boolean;
/** Grid all nodes will snap to
* @example [20, 20]
*/