refector viewport to use signals
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user