diff --git a/packages/svelte/src/lib/container/Panel/Panel.svelte b/packages/svelte/src/lib/container/Panel/Panel.svelte index 23d3f79e..86b8aa35 100644 --- a/packages/svelte/src/lib/container/Panel/Panel.svelte +++ b/packages/svelte/src/lib/container/Panel/Panel.svelte @@ -2,17 +2,18 @@ import cc from 'classcat'; import type { PanelProps } from './types'; - interface $$Props extends PanelProps {} + type $$Props = PanelProps; export let position: $$Props['position'] = 'top-right'; - export let style: $$Props['style'] = ''; + export let style: $$Props['style'] = undefined; + let className: $$Props['class'] = undefined; export { className as class }; $: positionClasses = `${position}`.split('-'); -
+
diff --git a/packages/svelte/src/lib/container/Panel/types.ts b/packages/svelte/src/lib/container/Panel/types.ts index 01f071da..32bbea01 100644 --- a/packages/svelte/src/lib/container/Panel/types.ts +++ b/packages/svelte/src/lib/container/Panel/types.ts @@ -1,6 +1,8 @@ import type { PanelPosition } from '@reactflow/system'; +import type { HTMLAttributes } from 'svelte/elements'; -export type PanelProps = { +export type PanelProps = HTMLAttributes & { + 'data-testid'?: string; position?: PanelPosition; style?: string; class?: string; diff --git a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte index a12fd924..7b7153b3 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte +++ b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte @@ -96,7 +96,7 @@ bind:this={domNode} style={style} class={cc(['svelte-flow', className])} - data-testid="rf__wrapper" + data-testid="svelte-flow__wrapper" on:dragover on:drop {...$$restProps} diff --git a/packages/svelte/src/lib/hooks/useSvelteFlow.ts b/packages/svelte/src/lib/hooks/useSvelteFlow.ts index cd246eea..d24c299f 100644 --- a/packages/svelte/src/lib/hooks/useSvelteFlow.ts +++ b/packages/svelte/src/lib/hooks/useSvelteFlow.ts @@ -1,22 +1,47 @@ -import type { Project, Viewport, XYPosition, ZoomInOut } from '@reactflow/system'; +import { get, writable, type Writable } from 'svelte/store'; +import type { + Project, + SetCenterOptions, + Viewport, + ViewportHelperFunctionOptions, + XYPosition, + ZoomInOut +} from '@reactflow/system'; +import { zoomIdentity } from 'd3-zoom'; import { useStore } from '$lib/store'; import type { FitViewOptions } from '$lib/types'; -import { get, writable, type Writable } from 'svelte/store'; import type { SvelteFlowStore } from '$lib/store/types'; -import { pointToRendererPoint } from '@reactflow/utils'; +import { getD3Transition, pointToRendererPoint } from '@reactflow/utils'; export function useSvelteFlow(): { zoomIn: ZoomInOut; zoomOut: ZoomInOut; + setZoom: (zoomLevel: number, options?: ViewportHelperFunctionOptions) => void; + getZoom: () => number; + setCenter: (x: number, y: number, options?: SetCenterOptions) => void; + setViewport: (viewport: Viewport, options?: ViewportHelperFunctionOptions) => void; + getViewport: () => Viewport; fitView: (options?: FitViewOptions) => void; + project: Project; viewport: Writable; nodes: SvelteFlowStore['nodes']; edges: SvelteFlowStore['edges']; - project: Project; } { // how to get the new context here? fit view doesn't work, because the store is not updated (uses old nodes store) - const { zoomIn, zoomOut, fitView, snapGrid: snapGridStore, transform, nodes, edges } = useStore(); + const { + zoomIn, + zoomOut, + fitView, + snapGrid, + transform, + width, + height, + maxZoom, + d3, + nodes, + edges + } = useStore(); const transformValues = get(transform); const viewportWritable = writable({ @@ -36,10 +61,53 @@ export function useSvelteFlow(): { return { zoomIn, zoomOut, + setZoom: (zoomLevel, options) => { + const { zoom, selection } = get(d3); + + if (zoom && selection) { + zoom.scaleTo(getD3Transition(selection, options?.duration), zoomLevel); + } + }, + getZoom: () => get(transform)[2], + setViewport: (viewport, options) => { + const [x, y, zoom] = get(transform); + const { zoom: d3Zoom, selection } = get(d3); + + if (d3Zoom && selection) { + const nextTransform = zoomIdentity + .translate(viewport.x ?? x, viewport.y ?? y) + .scale(viewport.zoom ?? zoom); + d3Zoom.transform(getD3Transition(selection, options?.duration), nextTransform); + } + }, + getViewport: () => { + const [x, y, zoom] = get(transform); + return { x, y, zoom }; + }, + setCenter: (x, y, options) => { + const _width = get(width); + const _height = get(height); + const _maxZoom = get(maxZoom); + const { zoom, selection } = get(d3); + + if (zoom && selection) { + const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : _maxZoom; + const centerX = _width / 2 - x * nextZoom; + const centerY = _height / 2 - y * nextZoom; + const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom); + + zoom.transform(getD3Transition(selection, options?.duration), transform); + } + }, fitView, project: (position: XYPosition) => { - const snapGrid = get(snapGridStore); - return pointToRendererPoint(position, get(transform), snapGrid !== null, snapGrid || [1, 1]); + const _snapGrid = get(snapGrid); + return pointToRendererPoint( + position, + get(transform), + _snapGrid !== null, + _snapGrid || [1, 1] + ); }, nodes, edges, diff --git a/packages/svelte/src/lib/plugins/Background/Background.svelte b/packages/svelte/src/lib/plugins/Background/Background.svelte index c8e74f72..908d3f6d 100644 --- a/packages/svelte/src/lib/plugins/Background/Background.svelte +++ b/packages/svelte/src/lib/plugins/Background/Background.svelte @@ -47,7 +47,7 @@ : [patternDimensions[0] / 2, patternDimensions[1] / 2]; - + - - + + {#if showZoom} 0 ? getBoundsOfRects(getRectOfNodes($nodes, $nodeOrigin), viewBB) : viewBB; + $nodes.length > 0 ? getBoundsOfRects(getRectOfNodes($nodes), viewBB) : viewBB; $: elementWidth = width ?? defaultWidth; $: elementHeight = height ?? defaultHeight; $: scaledWidth = boundingRect.width / elementWidth; @@ -71,6 +71,7 @@ {position} class={cc(['svelte-flow__minimap', className])} style={`background-color: ${bgColor}; ${style}`} + data-testid="svelte-flow__minimap" >
Functions:
+ + + +
Nodes:
{#each $nodes as node (node.id)}