refector viewport to use signals

This commit is contained in:
peterkogo
2024-12-06 15:35:43 +01:00
parent f29898f0bd
commit 4fcde6f6bf
19 changed files with 87 additions and 90 deletions
@@ -27,7 +27,7 @@
let id = 1; let id = 1;
const getId = () => `${id++}`; const getId = () => `${id++}`;
const { screenToFlowPosition, flowToScreenPosition } = useSvelteFlow(); const { screenToFlowPosition, flowToScreenPosition } = $derived(useSvelteFlow());
const handleConnectEnd: OnConnectEnd = (event) => { const handleConnectEnd: OnConnectEnd = (event) => {
if (!connectingNodeId) return; if (!connectingNodeId) return;
@@ -25,8 +25,7 @@ export default function drag(domNode: Element, params: UseDragParams) {
onDragStop, onDragStop,
onNodeMouseDown, onNodeMouseDown,
getStoreItems: () => { getStoreItems: () => {
const snapGrid = store.snapGrid; const { snapGrid, viewport } = store;
const vp = get(store.viewport);
return { return {
nodes: get(store.nodes), nodes: get(store.nodes),
@@ -38,7 +37,7 @@ export default function drag(domNode: Element, params: UseDragParams) {
nodeOrigin: store.nodeOrigin, nodeOrigin: store.nodeOrigin,
multiSelectionActive: store.multiselectionKeyPressed, multiSelectionActive: store.multiselectionKeyPressed,
domNode: store.domNode, domNode: store.domNode,
transform: [vp.x, vp.y, vp.zoom], transform: [viewport.x, viewport.y, viewport.zoom],
autoPanOnNodeDrag: store.autoPanOnNodeDrag, autoPanOnNodeDrag: store.autoPanOnNodeDrag,
nodesDraggable: store.nodesDraggable, nodesDraggable: store.nodesDraggable,
selectNodesOnDrag: store.selectNodesOnDrag, selectNodesOnDrag: store.selectNodesOnDrag,
+14 -6
View File
@@ -1,4 +1,3 @@
import type { Writable } from 'svelte/store';
import { import {
PanOnScrollMode, PanOnScrollMode,
XYPanZoom, XYPanZoom,
@@ -10,7 +9,7 @@ import {
} from '@xyflow/system'; } from '@xyflow/system';
type ZoomParams = { type ZoomParams = {
viewport: Writable<Viewport>; viewport: Viewport;
initialViewport: Viewport; initialViewport: Viewport;
minZoom: number; minZoom: number;
maxZoom: number; maxZoom: number;
@@ -45,11 +44,11 @@ export default function zoom(domNode: Element, params: ZoomParams) {
minZoom, minZoom,
maxZoom, maxZoom,
initialViewport, initialViewport,
viewport,
translateExtent, translateExtent,
paneClickDistance, paneClickDistance,
setPanZoomInstance, setPanZoomInstance,
onDraggingChange onDraggingChange,
onTransformChange
} = params; } = params;
const panZoomInstance = XYPanZoom({ const panZoomInstance = XYPanZoom({
@@ -61,8 +60,17 @@ export default function zoom(domNode: Element, params: ZoomParams) {
paneClickDistance, paneClickDistance,
onDraggingChange onDraggingChange
}); });
const currentViewport = panZoomInstance.getViewport();
viewport.set(currentViewport); //TODO: is this neccessary?
const viewport = panZoomInstance.getViewport();
if (
initialViewport.x !== viewport.x ||
initialViewport.y !== viewport.y ||
initialViewport.zoom !== viewport.zoom
) {
onTransformChange([viewport.x, viewport.y, viewport.zoom]);
}
setPanZoomInstance(panZoomInstance); setPanZoomInstance(panZoomInstance);
panZoomInstance.update(params); panZoomInstance.update(params);
@@ -40,7 +40,7 @@
); );
let store = useStore(); let store = useStore();
let { viewport, edges } = store; let { edges } = store;
function onPointerDown(event: MouseEvent | TouchEvent) { function onPointerDown(event: MouseEvent | TouchEvent) {
const isMouseTriggered = isMouseEvent(event); const isMouseTriggered = isMouseEvent(event);
@@ -81,7 +81,7 @@
onConnectEnd: (event, connectionState) => { onConnectEnd: (event, connectionState) => {
store.onconnectend?.(event, connectionState); store.onconnectend?.(event, connectionState);
}, },
getTransform: () => [$viewport.x, $viewport.y, $viewport.zoom], getTransform: () => [store.viewport.x, store.viewport.y, store.viewport.zoom],
getFromHandle: () => store.connection.fromHandle getFromHandle: () => store.connection.fromHandle
}); });
} }
@@ -45,7 +45,7 @@
children children
}: PaneProps = $props(); }: PaneProps = $props();
const { viewport, nodes, edges } = store; const { nodes, edges } = store;
// svelte-ignore non_reactive_update // svelte-ignore non_reactive_update
let container: HTMLDivElement; let container: HTMLDivElement;
@@ -131,7 +131,7 @@
selectedNodes = getNodesInside( selectedNodes = getNodesInside(
store.nodeLookup, store.nodeLookup,
nextUserSelectRect, nextUserSelectRect,
[$viewport.x, $viewport.y, $viewport.zoom], [store.viewport.x, store.viewport.y, store.viewport.zoom],
store.selectionMode === SelectionMode.Partial, store.selectionMode === SelectionMode.Partial,
true true
); );
@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { get } from 'svelte/store';
import { onMount, getContext, setContext } from 'svelte'; import { onMount, getContext, setContext } from 'svelte';
import cc from 'classcat'; import cc from 'classcat';
import { PanOnScrollMode } from '@xyflow/system'; import { PanOnScrollMode } from '@xyflow/system';
@@ -79,12 +78,9 @@
props, props,
container container
}); });
const { viewport } = store;
const initialViewport = props.initialViewport ?? get(viewport);
store.syncNodeStores(props.nodes); store.syncNodeStores(props.nodes);
store.syncEdgeStores(props.edges); store.syncEdgeStores(props.edges);
store.syncViewport(props.viewport);
const providerContext = getContext<ProviderContext>(key); const providerContext = getContext<ProviderContext>(key);
if (providerContext) { if (providerContext) {
@@ -128,7 +124,6 @@
/> />
<Zoom <Zoom
{store} {store}
{initialViewport}
{onMoveStart} {onMoveStart}
{onMove} {onMove}
{onMoveEnd} {onMoveEnd}
@@ -142,7 +137,7 @@
{paneClickDistance} {paneClickDistance}
> >
<Pane {store} {onpaneclick} {onpanecontextmenu} {panOnDrag} {selectionOnDrag}> <Pane {store} {onpaneclick} {onpanecontextmenu} {panOnDrag} {selectionOnDrag}>
<ViewportComponent viewport={$viewport}> <ViewportComponent {store}>
<EdgeRenderer <EdgeRenderer
{store} {store}
{onedgeclick} {onedgeclick}
@@ -1,13 +1,14 @@
<script lang="ts"> <script lang="ts">
import type { Viewport } from '@xyflow/system'; import type { SvelteFlowStore } from '$lib/store/types';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
let { viewport, children }: { viewport: Viewport; children: Snippet } = $props(); let { store, children }: { store: SvelteFlowStore; children: Snippet } = $props();
</script> </script>
<div <div
class="svelte-flow__viewport xyflow__viewport" class="svelte-flow__viewport xyflow__viewport"
style="transform: translate({viewport.x}px, {viewport.y}px) scale({viewport.zoom})" style="transform: translate({store.viewport.x}px, {store.viewport.y}px) scale({store.viewport
.zoom})"
> >
{@render children()} {@render children()}
</div> </div>
@@ -8,7 +8,6 @@
let { let {
store, store,
initialViewport = { x: 0, y: 0, zoom: 1 },
onMoveStart, onMoveStart,
onMove, onMove,
onMoveEnd, onMoveEnd,
@@ -27,7 +26,10 @@
let panOnScrollActive = $derived(store.panActivationKeyPressed || panOnScroll); let panOnScrollActive = $derived(store.panActivationKeyPressed || panOnScroll);
const onTransformChange = (transform: Transform) => const onTransformChange = (transform: Transform) =>
store.viewport.set({ x: transform[0], y: transform[1], zoom: transform[2] }); (store.viewport = { x: transform[0], y: transform[1], zoom: transform[2] });
// We extract the initial value by destructuring
const { viewport: initialViewport } = store;
onMount(() => { onMount(() => {
store.viewportInitialized = true; store.viewportInitialized = true;
@@ -1,10 +1,9 @@
import type { SvelteFlowStore } from '$lib/store/types'; import type { SvelteFlowStore } from '$lib/store/types';
import type { OnMoveStart, OnMove, OnMoveEnd, PanOnScrollMode, Viewport } from '@xyflow/system'; import type { OnMoveStart, OnMove, OnMoveEnd, PanOnScrollMode } from '@xyflow/system';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
export type ZoomProps = { export type ZoomProps = {
store: SvelteFlowStore; store: SvelteFlowStore;
initialViewport?: Viewport;
panOnScrollMode: PanOnScrollMode; panOnScrollMode: PanOnScrollMode;
onMove?: OnMove; onMove?: OnMove;
onMoveStart?: OnMoveStart; onMoveStart?: OnMoveStart;
@@ -1,4 +1,4 @@
import { get, type Writable } from 'svelte/store'; import { get } from 'svelte/store';
import { import {
getOverlappingArea, getOverlappingArea,
isRectObject, isRectObject,
@@ -197,7 +197,6 @@ export function useSvelteFlow(): {
* const clientPosition = flowToScreenPosition({ x: node.position.x, y: node.position.y }) * const clientPosition = flowToScreenPosition({ x: node.position.x, y: node.position.y })
*/ */
flowToScreenPosition: (flowPosition: XYPosition) => XYPosition; flowToScreenPosition: (flowPosition: XYPosition) => XYPosition;
viewport: Writable<Viewport>;
/** /**
* Updates a node. * Updates a node.
* *
@@ -279,7 +278,7 @@ export function useSvelteFlow(): {
} }
const store = useStore(); const store = useStore();
const { viewport, nodes, edges } = store; const { nodes, edges } = store;
const getNodeRect = (node: Node | { id: Node['id'] }): Rect | null => { const getNodeRect = (node: Node | { id: Node['id'] }): Rect | null => {
const nodeToUse = isNode(node) ? node : store.nodeLookup.get(node.id)!; const nodeToUse = isNode(node) ? node : store.nodeLookup.get(node.id)!;
@@ -378,9 +377,9 @@ export function useSvelteFlow(): {
? panZoom.scaleTo(zoomLevel, { duration: options?.duration }) ? panZoom.scaleTo(zoomLevel, { duration: options?.duration })
: Promise.resolve(false); : Promise.resolve(false);
}, },
getZoom: () => get(viewport).zoom, getZoom: () => store.viewport.zoom,
setViewport: async (nextViewport, options) => { setViewport: async (nextViewport, options) => {
const currentViewport = get(viewport); const currentViewport = store.viewport;
if (!store.panZoom) { if (!store.panZoom) {
return Promise.resolve(false); return Promise.resolve(false);
@@ -397,7 +396,7 @@ export function useSvelteFlow(): {
return Promise.resolve(true); return Promise.resolve(true);
}, },
getViewport: () => get(viewport), getViewport: () => store.viewport,
setCenter: async (x, y, options) => { setCenter: async (x, y, options) => {
const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : store.maxZoom; const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : store.maxZoom;
const currentPanZoom = store.panZoom; const currentPanZoom = store.panZoom;
@@ -513,7 +512,7 @@ export function useSvelteFlow(): {
} }
const _snapGrid = options.snapToGrid ? store.snapGrid : false; const _snapGrid = options.snapToGrid ? store.snapGrid : false;
const { x, y, zoom } = get(viewport); const { x, y, zoom } = store.viewport;
const { x: domX, y: domY } = store.domNode.getBoundingClientRect(); const { x: domX, y: domY } = store.domNode.getBoundingClientRect();
const correctedPosition = { const correctedPosition = {
x: position.x - domX, x: position.x - domX,
@@ -537,7 +536,7 @@ export function useSvelteFlow(): {
return position; return position;
} }
const { x, y, zoom } = get(viewport); const { x, y, zoom } = store.viewport;
const { x: domX, y: domY } = store.domNode.getBoundingClientRect(); const { x: domX, y: domY } = store.domNode.getBoundingClientRect();
const rendererPosition = rendererPointToPoint(position, [x, y, zoom]); const rendererPosition = rendererPointToPoint(position, [x, y, zoom]);
@@ -557,7 +556,7 @@ export function useSvelteFlow(): {
data: { ...node.data } data: { ...node.data }
})), })),
edges: get(edges).map((edge) => ({ ...edge })), edges: get(edges).map((edge) => ({ ...edge })),
viewport: { ...get(viewport) } viewport: { ...store.viewport }
}; };
}, },
updateNode, updateNode,
@@ -579,8 +578,7 @@ export function useSvelteFlow(): {
return getNodesBounds(nodes, { nodeLookup: store.nodeLookup, nodeOrigin: store.nodeOrigin }); return getNodesBounds(nodes, { nodeLookup: store.nodeLookup, nodeOrigin: store.nodeOrigin });
}, },
getHandleConnections: ({ type, id, nodeId }) => getHandleConnections: ({ type, id, nodeId }) =>
Array.from(store.connectionLookup.get(`${nodeId}-${type}-${id ?? null}`)?.values() ?? []), Array.from(store.connectionLookup.get(`${nodeId}-${type}-${id ?? null}`)?.values() ?? [])
viewport
}; };
} }
function getElements(lookup: Map<string, InternalNode>, ids: string[]): Node[]; function getElements(lookup: Map<string, InternalNode>, ids: string[]): Node[];
@@ -28,9 +28,6 @@
}: BackgroundProps = $props(); }: BackgroundProps = $props();
const store = useStore(); const store = useStore();
const { viewport } = store;
$inspect($viewport);
const patternSize = size ?? defaultSize[variant!]; const patternSize = size ?? defaultSize[variant!];
const isDots = variant === BackgroundVariant.Dots; const isDots = variant === BackgroundVariant.Dots;
@@ -38,8 +35,11 @@
const gapXY: number[] = Array.isArray(gap!) ? gap! : [gap!, gap!]; const gapXY: number[] = Array.isArray(gap!) ? gap! : [gap!, gap!];
let patternId = $derived(`background-pattern-${store.flowId}-${id ?? ''}`); let patternId = $derived(`background-pattern-${store.flowId}-${id ?? ''}`);
let scaledGap = $derived([gapXY[0] * $viewport.zoom || 1, gapXY[1] * $viewport.zoom || 1]); let scaledGap = $derived([
let scaledSize = $derived(patternSize * $viewport.zoom); gapXY[0] * store.viewport.zoom || 1,
gapXY[1] * store.viewport.zoom || 1
]);
let scaledSize = $derived(patternSize * store.viewport.zoom);
let patternDimensions = $derived( let patternDimensions = $derived(
(isCross ? [scaledSize, scaledSize] : scaledGap) as [number, number] (isCross ? [scaledSize, scaledSize] : scaledGap) as [number, number]
); );
@@ -56,8 +56,8 @@
> >
<pattern <pattern
id={patternId} id={patternId}
x={$viewport.x % scaledGap[0]} x={store.viewport.x % scaledGap[0]}
y={$viewport.y % scaledGap[1]} y={store.viewport.y % scaledGap[1]}
width={scaledGap[0]} width={scaledGap[0]}
height={scaledGap[1]} height={scaledGap[1]}
patternUnits="userSpaceOnUse" patternUnits="userSpaceOnUse"
@@ -33,7 +33,6 @@
}: ControlsProps = $props(); }: ControlsProps = $props();
const store = useStore(); const store = useStore();
const { viewport } = store;
const buttonProps = { const buttonProps = {
bgColor: buttonBgColor, bgColor: buttonBgColor,
@@ -46,8 +45,8 @@
let isInteractive = $state( let isInteractive = $state(
store.nodesDraggable || store.nodesConnectable || store.elementsSelectable store.nodesDraggable || store.nodesConnectable || store.elementsSelectable
); );
let minZoomReached = $derived($viewport.zoom <= store.minZoom); let minZoomReached = $derived(store.viewport.zoom <= store.minZoom);
let maxZoomReached = $derived($viewport.zoom >= store.maxZoom); let maxZoomReached = $derived(store.viewport.zoom >= store.maxZoom);
let orientationClass = $derived(orientation === 'horizontal' ? 'horizontal' : 'vertical'); let orientationClass = $derived(orientation === 'horizontal' ? 'horizontal' : 'vertical');
const onZoomInHandler = () => { const onZoomInHandler = () => {
@@ -43,7 +43,6 @@
}: MiniMapProps = $props(); }: MiniMapProps = $props();
let store = useStore(); let store = useStore();
const { viewport } = store;
let nodes = $derived(store.nodes); let nodes = $derived(store.nodes);
@@ -58,10 +57,10 @@
// TODO: simplify this // TODO: simplify this
let viewBB = $derived({ let viewBB = $derived({
x: -$viewport.x / $viewport.zoom, x: -store.viewport.x / store.viewport.zoom,
y: -$viewport.y / $viewport.zoom, y: -store.viewport.y / store.viewport.zoom,
width: store.width / $viewport.zoom, width: store.width / store.viewport.zoom,
height: store.height / $viewport.zoom height: store.height / store.viewport.zoom
}); });
let boundingRect = $derived( let boundingRect = $derived(
store.nodeLookup.size > 0 store.nodeLookup.size > 0
@@ -102,8 +101,8 @@
? maskStrokeWidth * viewScale ? maskStrokeWidth * viewScale
: undefined} : undefined}
use:interactive={{ use:interactive={{
store,
panZoom: store.panZoom, panZoom: store.panZoom,
viewport: store.viewport,
getViewScale, getViewScale,
translateExtent: store.translateExtent, translateExtent: store.translateExtent,
width: store.width, width: store.width,
@@ -1,14 +1,9 @@
import { get, type Writable } from 'svelte/store'; import { XYMinimap, type PanZoomInstance, type XYMinimapUpdate } from '@xyflow/system';
import { import type { SvelteFlowStore } from '$lib/store/types';
XYMinimap,
type PanZoomInstance,
type Viewport,
type XYMinimapUpdate
} from '@xyflow/system';
export type UseInteractiveParams = { export type UseInteractiveParams = {
panZoom: PanZoomInstance; panZoom: PanZoomInstance;
viewport: Writable<Viewport>; store: SvelteFlowStore;
getViewScale: () => number; getViewScale: () => number;
} & XYMinimapUpdate; } & XYMinimapUpdate;
@@ -17,7 +12,7 @@ export default function interactive(domNode: Element, params: UseInteractivePara
domNode, domNode,
panZoom: params.panZoom, panZoom: params.panZoom,
getTransform: () => { getTransform: () => {
const viewport = get(params.viewport); const { viewport } = params.store;
return [viewport.x, viewport.y, viewport.zoom]; return [viewport.x, viewport.y, viewport.zoom];
}, },
getViewScale: params.getViewScale getViewScale: params.getViewScale
@@ -11,6 +11,7 @@
type XYResizerChildChange type XYResizerChildChange
} from '@xyflow/system'; } from '@xyflow/system';
import type { ResizeControlProps } from './types'; import type { ResizeControlProps } from './types';
import { get } from 'svelte/store';
let { let {
nodeId, nodeId,
@@ -31,7 +32,7 @@
children children
}: ResizeControlProps = $props(); }: ResizeControlProps = $props();
const { nodeLookup, snapGrid, viewport, nodes, nodeOrigin, domNode } = useStore(); const store = useStore();
let id = $derived( let id = $derived(
typeof nodeId === 'string' ? nodeId : getContext<string>('svelteflow__node_id') typeof nodeId === 'string' ? nodeId : getContext<string>('svelteflow__node_id')
@@ -62,16 +63,16 @@
nodeId: id, nodeId: id,
getStoreItems: () => { getStoreItems: () => {
return { return {
nodeLookup: $nodeLookup, nodeLookup: store.nodeLookup,
transform: [$viewport.x, $viewport.y, $viewport.zoom], transform: [store.viewport.x, store.viewport.y, store.viewport.zoom],
snapGrid: $snapGrid ?? undefined, snapGrid: store.snapGrid ?? undefined,
snapToGrid: !!$snapGrid, snapToGrid: !!store.snapGrid,
nodeOrigin: $nodeOrigin, nodeOrigin: store.nodeOrigin,
paneDomNode: $domNode paneDomNode: store.domNode
}; };
}, },
onChange: (change: XYResizerChange, childChanges: XYResizerChildChange[]) => { onChange: (change: XYResizerChange, childChanges: XYResizerChildChange[]) => {
const node = $nodeLookup.get(id)?.internals.userNode; const node = store.nodeLookup.get(id)?.internals.userNode;
if (!node) { if (!node) {
return; return;
} }
@@ -86,13 +87,13 @@
} }
for (const childChange of childChanges) { for (const childChange of childChanges) {
const childNode = $nodeLookup.get(childChange.id)?.internals.userNode; const childNode = store.nodeLookup.get(childChange.id)?.internals.userNode;
if (childNode) { if (childNode) {
childNode.position = childChange.position; childNode.position = childChange.position;
} }
} }
$nodes = $nodes; store.nodes.set(get(store.nodes));
} }
}); });
} }
@@ -18,7 +18,8 @@
children children
}: NodeToolbarProps = $props(); }: NodeToolbarProps = $props();
const { domNode, viewport, nodeLookup, nodes } = useStore(); const store = useStore();
const { nodes } = store;
const { getNodesBounds } = useSvelteFlow(); const { getNodesBounds } = useSvelteFlow();
const contextNodeId = getContext<string>('svelteflow__node_id'); const contextNodeId = getContext<string>('svelteflow__node_id');
@@ -28,7 +29,7 @@
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId ?? contextNodeId]; const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId ?? contextNodeId];
return nodeIds.reduce<InternalNode[]>((res, nodeId) => { return nodeIds.reduce<InternalNode[]>((res, nodeId) => {
const node = $nodeLookup.get(nodeId); const node = store.nodeLookup.get(nodeId);
if (node) { if (node) {
res.push(node); res.push(node);
@@ -41,7 +42,7 @@
let transform: string = $derived.by(() => { let transform: string = $derived.by(() => {
const nodeRect = getNodesBounds(toolbarNodes); const nodeRect = getNodesBounds(toolbarNodes);
if (nodeRect) { if (nodeRect) {
return getNodeToolbarTransform(nodeRect, $viewport, position, offset, align); return getNodeToolbarTransform(nodeRect, store.viewport, position, offset, align);
} }
return ''; return '';
}); });
@@ -63,11 +64,11 @@
); );
</script> </script>
{#if $domNode && isActive && toolbarNodes} {#if store.domNode && isActive && toolbarNodes}
<div <div
data-id={toolbarNodes.reduce((acc, node) => `${acc}${node.id} `, '').trim()} data-id={toolbarNodes.reduce((acc, node) => `${acc}${node.id} `, '').trim()}
class="svelte-flow__node-toolbar" class="svelte-flow__node-toolbar"
use:portal={{ domNode: $domNode }} use:portal={{ domNode: store.domNode }}
style:position="absolute" style:position="absolute"
style:transform style:transform
style:z-index={zIndex} style:z-index={zIndex}
+3 -4
View File
@@ -22,7 +22,7 @@ import {
import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types'; import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types';
import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store.svelte'; import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store.svelte';
import { type StoreSignals, type SvelteFlowStore, type SvelteFlowStoreActions } from './types'; import { type StoreSignals, type SvelteFlowStore, type SvelteFlowStoreActions } from './types';
import { syncNodeStores, syncEdgeStores, syncViewportStores } from './utils'; import { syncNodeStores, syncEdgeStores } from './utils';
// import { getVisibleEdges } from './visible-edges'; // import { getVisibleEdges } from './visible-edges';
// import { getVisibleNodes } from './visible-nodes'; // import { getVisibleNodes } from './visible-nodes';
@@ -314,11 +314,10 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
} }
function panBy(delta: XYPosition) { function panBy(delta: XYPosition) {
const viewport = get(store.viewport);
return panBySystem({ return panBySystem({
delta, delta,
panZoom: store.panZoom, panZoom: store.panZoom,
transform: [viewport.x, viewport.y, viewport.zoom], transform: [store.viewport.x, store.viewport.y, store.viewport.zoom],
translateExtent: store.translateExtent, translateExtent: store.translateExtent,
width: store.width, width: store.width,
height: store.height height: store.height
@@ -346,7 +345,7 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
const storeWithActions = Object.assign(store, { const storeWithActions = Object.assign(store, {
syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes), syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes),
syncEdgeStores: (edges) => syncEdgeStores(store.edges, edges), syncEdgeStores: (edges) => syncEdgeStores(store.edges, edges),
syncViewport: (viewport) => syncViewportStores(store.panZoom, store.viewport, viewport), // syncViewport: (viewport) => syncViewportStores(store.panZoom, store.viewport, viewport),
setNodeTypes, setNodeTypes,
setEdgeTypes, setEdgeTypes,
addEdge, addEdge,
@@ -118,9 +118,7 @@ export const getInitialStore = (signals: StoreSignals) => {
nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes }); nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes });
edgeTypes: EdgeTypes = $derived({ ...initialEdgeTypes, ...signals.props.edgeTypes }); edgeTypes: EdgeTypes = $derived({ ...initialEdgeTypes, ...signals.props.edgeTypes });
viewport: Writable<Viewport> = writable( viewport: Viewport = $state(signals.props.initialViewport ?? { x: 0, y: 0, zoom: 1 });
signals.props.initialViewport ?? { x: 0, y: 0, zoom: 1 }
);
connectionMode: ConnectionMode = $derived( connectionMode: ConnectionMode = $derived(
signals.props.connectionMode ?? ConnectionMode.Strict signals.props.connectionMode ?? ConnectionMode.Strict
@@ -128,10 +126,13 @@ export const getInitialStore = (signals: StoreSignals) => {
rawConnection: ConnectionState = $state(initialConnection); rawConnection: ConnectionState = $state(initialConnection);
connection: ConnectionState = $derived.by(() => { connection: ConnectionState = $derived.by(() => {
if (this.rawConnection.inProgress) { if (this.rawConnection.inProgress) {
const viewport = get(this.viewport);
return { return {
...this.rawConnection, ...this.rawConnection,
to: pointToRendererPoint(this.rawConnection.to, [viewport.x, viewport.y, viewport.zoom]) to: pointToRendererPoint(this.rawConnection.to, [
this.viewport.x,
this.viewport.y,
this.viewport.zoom
])
}; };
} else { } else {
return { ...this.rawConnection }; return { ...this.rawConnection };
@@ -210,12 +211,12 @@ export const getInitialStore = (signals: StoreSignals) => {
this.edges = createEdgesStore(edges, this.connectionLookup, this.edgeLookup); this.edges = createEdgesStore(edges, this.connectionLookup, this.edgeLookup);
updateConnectionLookup(this.connectionLookup, this.edgeLookup, edges); updateConnectionLookup(this.connectionLookup, this.edgeLookup, edges);
if (signals.props.fitView && this.width && this.height) { if (signals.props.fitView && !signals.props.initialViewport && this.width && this.height) {
const bounds = getInternalNodesBounds(this.nodeLookup, { const bounds = getInternalNodesBounds(this.nodeLookup, {
filter: (node) => filter: (node) =>
!!((node.width || node.initialWidth) && (node.height || node.initialHeight)) !!((node.width || node.initialWidth) && (node.height || node.initialHeight))
}); });
this.viewport.set(getViewportForBounds(bounds, this.width, this.height, 0.5, 2, 0.1)); this.viewport = getViewportForBounds(bounds, this.width, this.height, 0.5, 2, 0.1);
} }
} }
+1 -1
View File
@@ -17,7 +17,7 @@ import type { SvelteFlowProps } from '$lib/container/SvelteFlow';
export type SvelteFlowStoreActions = { export type SvelteFlowStoreActions = {
syncNodeStores: (nodesStore: Writable<Node[]>) => void; syncNodeStores: (nodesStore: Writable<Node[]>) => void;
syncEdgeStores: (edgeStore: Writable<Edge[]>) => void; syncEdgeStores: (edgeStore: Writable<Edge[]>) => void;
syncViewport: (viewportStore?: Writable<Viewport>) => void; // syncViewport: (viewportStore?: Writable<Viewport>) => void;
setNodeTypes: (nodeTypes: NodeTypes) => void; setNodeTypes: (nodeTypes: NodeTypes) => void;
setEdgeTypes: (edgeTypes: EdgeTypes) => void; setEdgeTypes: (edgeTypes: EdgeTypes) => void;
addEdge: (edge: Edge | Connection) => void; addEdge: (edge: Edge | Connection) => void;