From 24735aa003fd0055a116353ffebc8348e6b4f00c Mon Sep 17 00:00:00 2001 From: peterkogo Date: Wed, 11 Dec 2024 15:22:44 +0100 Subject: [PATCH] Reworked edge rerendering --- .../src/routes/examples/stress/+page.svelte | 2 +- packages/svelte/src/lib/actions/drag/index.ts | 1 - .../components/EdgeWrapper/EdgeWrapper.svelte | 60 +++++++++--------- .../components/NodeWrapper/NodeWrapper.svelte | 20 +++--- .../src/lib/components/NodeWrapper/types.ts | 36 +---------- .../EdgeRenderer/EdgeRenderer.svelte | 61 +++++++------------ .../NodeRenderer/NodeRenderer.svelte | 1 + packages/system/src/utils/store.ts | 22 +++---- 8 files changed, 79 insertions(+), 124 deletions(-) diff --git a/examples/svelte/src/routes/examples/stress/+page.svelte b/examples/svelte/src/routes/examples/stress/+page.svelte index 5313a690..13b7ea89 100644 --- a/examples/svelte/src/routes/examples/stress/+page.svelte +++ b/examples/svelte/src/routes/examples/stress/+page.svelte @@ -48,7 +48,7 @@ } let nodes = $state.raw(nodeItems); - let edges = $state.raw([]); + let edges = $state.raw(edgeItems); diff --git a/packages/svelte/src/lib/actions/drag/index.ts b/packages/svelte/src/lib/actions/drag/index.ts index ec8e5307..4f4e26d9 100644 --- a/packages/svelte/src/lib/actions/drag/index.ts +++ b/packages/svelte/src/lib/actions/drag/index.ts @@ -1,4 +1,3 @@ -import { get } from 'svelte/store'; import { XYDrag, type OnDrag } from '@xyflow/system'; import type { SvelteFlowStore } from '$lib/store/types'; diff --git a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte index f6bc36a0..faad3aec 100644 --- a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte +++ b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte @@ -11,51 +11,55 @@ import type { SvelteFlowStore } from '$lib/store/types'; const { - id, - type = 'default', + edge, store, + onedgeclick, + onedgecontextmenu, + onedgemouseenter, + onedgemouseleave + }: { store: SvelteFlowStore; edge: EdgeLayouted } & EdgeEvents = $props(); + + let { source, target, - data = {}, - style, - zIndex, - animated = false, - selected = false, - selectable, - deletable, - hidden, - label, - labelStyle, - markerStart, - markerEnd, - sourceHandle, - targetHandle, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, - ariaLabel, + animated, + selected, + label, + labelStyle, + data = {}, + style, interactionWidth, + type = 'default', + sourceHandle, + targetHandle, + markerStart, + markerEnd, + selectable: edgeSelectable, + deletable, + hidden, + zIndex, class: className, - onedgeclick, - onedgecontextmenu, - onedgemouseenter, - onedgemouseleave - }: { store: SvelteFlowStore } & EdgeLayouted & EdgeEvents = $props(); + ariaLabel + } = $derived(edge); + const { id } = edge; setContext('svelteflow__edge_id', id); - let edgeType = $derived(type ?? 'default'); - let EdgeComponent = $derived(store.edgeTypes[edgeType] ?? BezierEdgeInternal); + let selectable = $derived(edgeSelectable ?? store.elementsSelectable); + let EdgeComponent = $derived(store.edgeTypes[type] ?? BezierEdgeInternal); + let markerStartUrl = $derived( markerStart ? `url('#${getMarkerId(markerStart, store.flowId)}')` : undefined ); let markerEndUrl = $derived( markerEnd ? `url('#${getMarkerId(markerEnd, store.flowId)}')` : undefined ); - let isSelectable = $derived(selectable ?? store.elementsSelectable); const handleEdgeSelect = useHandleEdgeSelect(); @@ -88,7 +92,7 @@ class={cc(['svelte-flow__edge', className])} class:animated class:selected - class:selectable={isSelectable} + class:selectable data-id={id} {onclick} oncontextmenu={onedgecontextmenu @@ -130,9 +134,9 @@ {data} {style} {interactionWidth} - selectable={isSelectable} + {selectable} deletable={deletable ?? true} - type={edgeType} + {type} sourceHandleId={sourceHandle} targetHandleId={targetHandle} markerStart={markerStartUrl} diff --git a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte index 33530ceb..96885848 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte +++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte @@ -4,9 +4,10 @@ import { errorMessages, nodeHasDimensions, Position } from '@xyflow/system'; import drag from '$lib/actions/drag'; - import DefaultNode from '$lib/components/nodes/DefaultNode.svelte'; - import type { ConnectableContext, NodeWrapperProps } from './types'; import { getNodeInlineStyleDimensions } from './utils'; + import DefaultNode from '$lib/components/nodes/DefaultNode.svelte'; + + import type { ConnectableContext, NodeWrapperProps } from './types'; import type { Node, NodeEvents } from '$lib/types'; let { @@ -25,7 +26,6 @@ }: NodeWrapperProps & NodeEvents = $props(); let { - id, data = {}, selected = false, draggable: _draggable, @@ -48,18 +48,16 @@ dragHandle } = $derived(node); + let { id } = node; + let draggable = $derived(_draggable ?? store.nodesDraggable); let selectable = $derived(_selectable ?? store.elementsSelectable); let connectable = $derived(_connectable ?? store.nodesConnectable); let initialized = $derived(nodeHasDimensions(node)); - // TODO: does this make sense - let flipFlop = false; - let shouldRerenderSignal = $derived.by(() => - store.adoptNodes.has(id) ? (flipFlop = !flipFlop) : flipFlop - ); - - function getInternalNode(node: Node, shouldRerender: boolean = false) { + // we also pass store.nodes to this function to rerender when a node changes + // eslint-disable-next-line @typescript-eslint/no-unused-vars + function getInternalNode(node: Node, nodes: Node[]) { return { ...store.nodeLookup.get(node.id)! }; } @@ -68,7 +66,7 @@ z: zIndex = 0, positionAbsolute: { x: positionX, y: positionY } } - } = $derived(getInternalNode(node, shouldRerenderSignal)); + } = $derived(getInternalNode(node, store.nodes)); function isInParentLookup(id: string) { return store.parentLookup.has(id); diff --git a/packages/svelte/src/lib/components/NodeWrapper/types.ts b/packages/svelte/src/lib/components/NodeWrapper/types.ts index 93a3ddf1..2e06e63f 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/types.ts +++ b/packages/svelte/src/lib/components/NodeWrapper/types.ts @@ -1,45 +1,13 @@ import type { SvelteFlowStore } from '$lib/store/types'; -import type { InternalNode, Node } from '$lib/types'; +import type { Node } from '$lib/types'; export type ConnectableContext = { value: boolean; }; export type NodeWrapperProps = { - // Pick< - // Node, - // | 'id' - // | 'class' - // | 'connectable' - // | 'data' - // | 'draggable' - // | 'dragging' - // | 'selected' - // | 'selectable' - // | 'deletable' - // | 'style' - // | 'type' - // | 'sourcePosition' - // | 'targetPosition' - // | 'dragHandle' - // | 'hidden' - // | 'width' - // | 'height' - // | 'initialWidth' - // | 'initialHeight' - // | 'parentId' - // > & { - // measuredWidth?: number; - // measuredHeight?: number; - // type: string; - // positionX: number; - // positionY: number; - // 'on:nodeclick'?: (event: MouseEvent) => void; - // isParent?: boolean; - // zIndex: number; - // initialized: boolean; node: Node; + store: SvelteFlowStore; nodeClickDistance?: number; resizeObserver?: ResizeObserver | null; - store: SvelteFlowStore; }; diff --git a/packages/svelte/src/lib/container/EdgeRenderer/EdgeRenderer.svelte b/packages/svelte/src/lib/container/EdgeRenderer/EdgeRenderer.svelte index f03e06d3..70ca8370 100644 --- a/packages/svelte/src/lib/container/EdgeRenderer/EdgeRenderer.svelte +++ b/packages/svelte/src/lib/container/EdgeRenderer/EdgeRenderer.svelte @@ -14,16 +14,26 @@ onedgemouseleave }: { store: SvelteFlowStore } & EdgeEvents = $props(); - // const { edges, nodes } = store; - + let previousLayoutedEdges = new Map(); let layoutedEdges = $derived.by(() => { store.nodes; - const layoutedEdges = store.edges.reduce((res, edge) => { + + const layoutedEdges = new Map(); + + for (let edge of store.edges) { const sourceNode = store.nodeLookup.get(edge.source); const targetNode = store.nodeLookup.get(edge.target); if (!sourceNode || !targetNode) { - return res; + continue; + } + + // we reuse the previous edge object if the source and target node are the same as before + // references to internalNodes that haven't changed stay the same + const previous = previousLayoutedEdges.get(edge.id); + if (previous && sourceNode == previous.sourceNode && targetNode == previous.targetNode) { + layoutedEdges.set(edge.id, previous); + continue; } const edgePosition = getEdgePosition({ @@ -37,7 +47,7 @@ }); if (edgePosition) { - res.push({ + layoutedEdges.set(edge.id, { ...edge, zIndex: getElevatedEdgeZIndex({ selected: edge.selected, @@ -46,13 +56,14 @@ targetNode, elevateOnSelect: false }), - ...edgePosition + ...edgePosition, + sourceNode, + targetNode }); } + } - return res; - }, []); - + previousLayoutedEdges = layoutedEdges; return layoutedEdges; }); @@ -62,36 +73,10 @@ - {#each layoutedEdges as edge (edge.id)} + {#each layoutedEdges.values() as edge (edge.id)}