From 7104872a12ab2840c4ba545fe6a23de06ee192a0 Mon Sep 17 00:00:00 2001 From: saswata <125185423+saswatax@users.noreply.github.com> Date: Sat, 9 Mar 2024 15:43:37 +0530 Subject: [PATCH 01/14] fix(useNodesData): fix crash when passing invalid node id --- packages/system/src/utils/shallow-node-data.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/system/src/utils/shallow-node-data.ts b/packages/system/src/utils/shallow-node-data.ts index 2faf1c1a..42af404e 100644 --- a/packages/system/src/utils/shallow-node-data.ts +++ b/packages/system/src/utils/shallow-node-data.ts @@ -2,7 +2,11 @@ import { NodeBase } from '../types'; type NodeData = Pick; -export function shallowNodeData(a: NodeData | NodeData[], b: NodeData | NodeData[]) { +export function shallowNodeData(a: NodeData | null | NodeData[], b: NodeData | null | NodeData[]) { + if (a === null || b === null) { + return false; + } + const _a = Array.isArray(a) ? a : [a]; const _b = Array.isArray(b) ? b : [b]; From b761859aa008c5ea181020befbdf7be1218037e4 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 11 Mar 2024 10:59:53 +0100 Subject: [PATCH 02/14] added nodedrag events to selection box --- .../NodeSelection/NodeSelection.svelte | 16 ++++++++++- .../components/NodeWrapper/NodeWrapper.svelte | 28 +++++++------------ .../container/SvelteFlow/SvelteFlow.svelte | 8 +++++- packages/svelte/src/lib/utils/index.ts | 13 +++++++++ 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte b/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte index b314e2ad..4e324a12 100644 --- a/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte +++ b/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte @@ -6,6 +6,7 @@ import { Selection } from '$lib/components/Selection'; import drag from '$lib/actions/drag'; import type { Node } from '$lib/types'; + import { createNodeEventDispatcher } from '$lib'; const store = useStore(); const { selectionRectMode, nodes } = store; @@ -14,6 +15,7 @@ selectioncontextmenu: { nodes: Node[]; event: MouseEvent | TouchEvent }; selectionclick: { nodes: Node[]; event: MouseEvent | TouchEvent }; }>(); + const dispatchNodeEvent = createNodeEventDispatcher(); $: selectedNodes = $nodes.filter((n) => n.selected); $: bounds = getNodesBounds(selectedNodes); @@ -31,7 +33,19 @@
{ + dispatchNodeEvent('nodedrag', { event, node, nodes }); + }, + onDragStart: (event, _, node, nodes) => { + dispatchNodeEvent('nodedragstart', { event, node, nodes }); + }, + onDragStop: (event, _, node, nodes) => { + dispatchNodeEvent('nodedragstop', { event, node, nodes }); + } + }} on:contextmenu={onContextMenu} on:click={onClick} > diff --git a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte index 04dcfc8c..0b7305ff 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte +++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte @@ -12,6 +12,7 @@ import type { NodeWrapperProps } from './types'; import type { Node } from '$lib/types'; import { getNodeInlineStyleDimensions } from './utils'; + import { createNodeEventDispatcher } from '$lib'; interface $$Props extends NodeWrapperProps {} @@ -66,16 +67,7 @@ } const nodeComponent = $nodeTypes[nodeType] || DefaultNode; - const dispatch = createEventDispatcher<{ - nodeclick: { node: Node; event: MouseEvent | TouchEvent }; - nodecontextmenu: { node: Node; event: MouseEvent | TouchEvent }; - nodedrag: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent }; - nodedragstart: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent }; - nodedragstop: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent }; - nodemouseenter: { node: Node; event: MouseEvent | TouchEvent }; - nodemouseleave: { node: Node; event: MouseEvent | TouchEvent }; - nodemousemove: { node: Node; event: MouseEvent | TouchEvent }; - }>(); + const dispatchNodeEvent = createNodeEventDispatcher(); const connectableStore = writable(connectable); let prevType: string | undefined = undefined; let prevSourcePosition: Position | undefined = undefined; @@ -151,7 +143,7 @@ handleNodeSelection(id); } - dispatch('nodeclick', { node, event }); + dispatchNodeEvent('nodeclick', { node, event }); } @@ -167,13 +159,13 @@ noDragClass: 'nodrag', onNodeMouseDown: handleNodeSelection, onDrag: (event, _, node, nodes) => { - dispatch('nodedrag', { event, node, nodes }); + dispatchNodeEvent('nodedrag', { event, node, nodes }); }, onDragStart: (event, _, node, nodes) => { - dispatch('nodedragstart', { event, node, nodes }); + dispatchNodeEvent('nodedragstart', { event, node, nodes }); }, onDragStop: (event, _, node, nodes) => { - dispatch('nodedragstop', { event, node, nodes }); + dispatchNodeEvent('nodedragstop', { event, node, nodes }); }, store }} @@ -192,10 +184,10 @@ style:visibility={initialized ? 'visible' : 'hidden'} style="{style ?? ''};{inlineStyleDimensions.width}{inlineStyleDimensions.height}" on:click={onSelectNodeHandler} - on:mouseenter={(event) => dispatch('nodemouseenter', { node, event })} - on:mouseleave={(event) => dispatch('nodemouseleave', { node, event })} - on:mousemove={(event) => dispatch('nodemousemove', { node, event })} - on:contextmenu={(event) => dispatch('nodecontextmenu', { node, event })} + on:mouseenter={(event) => dispatchNodeEvent('nodemouseenter', { node, event })} + on:mouseleave={(event) => dispatchNodeEvent('nodemouseleave', { node, event })} + on:mousemove={(event) => dispatchNodeEvent('nodemousemove', { node, event })} + on:contextmenu={(event) => dispatchNodeEvent('nodecontextmenu', { node, event })} > - + diff --git a/packages/svelte/src/lib/utils/index.ts b/packages/svelte/src/lib/utils/index.ts index 10f205ca..d5da3085 100644 --- a/packages/svelte/src/lib/utils/index.ts +++ b/packages/svelte/src/lib/utils/index.ts @@ -1,3 +1,4 @@ +import { createEventDispatcher } from 'svelte'; import { isNodeBase, isEdgeBase } from '@xyflow/system'; import type { Edge, Node } from '$lib/types'; @@ -21,3 +22,15 @@ export const isNode = (element: unknown): element */ export const isEdge = (element: unknown): element is EdgeType => isEdgeBase(element); + +export const createNodeEventDispatcher = () => + createEventDispatcher<{ + nodeclick: { node: Node; event: MouseEvent | TouchEvent }; + nodecontextmenu: { node: Node; event: MouseEvent | TouchEvent }; + nodedrag: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent }; + nodedragstart: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent }; + nodedragstop: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent }; + nodemouseenter: { node: Node; event: MouseEvent | TouchEvent }; + nodemouseleave: { node: Node; event: MouseEvent | TouchEvent }; + nodemousemove: { node: Node; event: MouseEvent | TouchEvent }; + }>(); From a224b88c658c0dfdea2c7fb5372ddb59e2a15c0a Mon Sep 17 00:00:00 2001 From: peterkogo Date: Wed, 13 Mar 2024 13:56:18 +0100 Subject: [PATCH 03/14] connectionindicator class is now added if handle can be connected to based on handle types --- .../src/lib/components/Handle/Handle.svelte | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/svelte/src/lib/components/Handle/Handle.svelte b/packages/svelte/src/lib/components/Handle/Handle.svelte index e35804fc..a492d0c0 100644 --- a/packages/svelte/src/lib/components/Handle/Handle.svelte +++ b/packages/svelte/src/lib/components/Handle/Handle.svelte @@ -8,7 +8,8 @@ isMouseEvent, type HandleConnection, areConnectionMapsEqual, - handleConnectionChange + handleConnectionChange, + ConnectionMode } from '@xyflow/system'; import { useStore } from '$lib/store'; @@ -32,7 +33,7 @@ const isTarget = type === 'target'; const nodeId = getContext('svelteflow__node_id'); const connectable = getContext>('svelteflow__node_connectable'); - $: handleConnectable = isConnectable !== undefined ? isConnectable : $connectable; + $: isConnectable = isConnectable !== undefined ? isConnectable : $connectable; const handleId = id || null; @@ -124,6 +125,7 @@ prevConnections = connections ?? new Map(); } + $: connectionInProcess = !!$connection.startHandle; $: connectingFrom = $connection.startHandle?.nodeId === nodeId && $connection.startHandle?.type === type && @@ -132,6 +134,11 @@ $connection.endHandle?.nodeId === nodeId && $connection.endHandle?.type === type && $connection.endHandle?.handleId === handleId; + $: isPossibleEndHandle = + $connectionMode === ConnectionMode.Strict + ? $connection.startHandle?.type !== type + : nodeId !== $connection.startHandle?.nodeId || + handleId !== $connection.startHandle?.handleId; $: valid = connectingTo && $connection.status === 'valid'; // @todo implement connectablestart, connectableend @@ -161,10 +168,10 @@ The Handle component is the part of a node that can be used to connect nodes. ])} class:source={!isTarget} class:target={isTarget} - class:connectablestart={handleConnectable} - class:connectableend={handleConnectable} - class:connectable={handleConnectable} - class:connectionindicator={handleConnectable} + class:connectablestart={isConnectable} + class:connectableend={isConnectable} + class:connectable={isConnectable} + class:connectionindicator={isConnectable && (!connectionInProcess || isPossibleEndHandle)} on:mousedown={onPointerDown} on:touchstart={onPointerDown} {style} From e489df9808ff176553d175d03a91c43373e5adc0 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Wed, 13 Mar 2024 14:26:27 +0100 Subject: [PATCH 04/14] added check to xyhandle pointermove if connection is cancelled --- packages/react/src/components/Handle/index.tsx | 1 + packages/svelte/src/lib/components/Handle/Handle.svelte | 3 ++- packages/system/src/xyhandle/XYHandle.ts | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/react/src/components/Handle/index.tsx b/packages/react/src/components/Handle/index.tsx index f1f84cce..f481b7ce 100644 --- a/packages/react/src/components/Handle/index.tsx +++ b/packages/react/src/components/Handle/index.tsx @@ -140,6 +140,7 @@ function HandleComponent( onConnect: onConnectExtended, isValidConnection: isValidConnection || currentStore.isValidConnection, getTransform: () => store.getState().transform, + getConnectionStartHandle: () => store.getState().connectionStartHandle, }); } diff --git a/packages/svelte/src/lib/components/Handle/Handle.svelte b/packages/svelte/src/lib/components/Handle/Handle.svelte index e35804fc..1dd64671 100644 --- a/packages/svelte/src/lib/components/Handle/Handle.svelte +++ b/packages/svelte/src/lib/components/Handle/Handle.svelte @@ -99,7 +99,8 @@ onConnectEnd: (event) => { $onConnectEndAction?.(event); }, - getTransform: () => [$viewport.x, $viewport.y, $viewport.zoom] + getTransform: () => [$viewport.x, $viewport.y, $viewport.zoom], + getConnectionStartHandle: () => $connection.startHandle }); } } diff --git a/packages/system/src/xyhandle/XYHandle.ts b/packages/system/src/xyhandle/XYHandle.ts index 4f99a97c..638ec966 100644 --- a/packages/system/src/xyhandle/XYHandle.ts +++ b/packages/system/src/xyhandle/XYHandle.ts @@ -38,6 +38,7 @@ export type OnPointerDownParams = { isValidConnection?: IsValidConnection; onEdgeUpdateEnd?: (evt: MouseEvent | TouchEvent) => void; getTransform: () => Transform; + getConnectionStartHandle: () => ConnectingHandle | null; }; export type IsValidParams = { @@ -91,6 +92,7 @@ function onPointerDown( onEdgeUpdateEnd, updateConnection, getTransform, + getConnectionStartHandle, }: OnPointerDownParams ) { // when xyflow is used inside a shadow root we can't use document @@ -149,6 +151,10 @@ function onPointerDown( onConnectStart?.(event, { nodeId, handleId, handleType }); function onPointerMove(event: MouseEvent | TouchEvent) { + if (!getConnectionStartHandle()) { + onPointerUp(event); + } + const transform = getTransform(); connectionPosition = getEventPosition(event, containerBounds); closestHandle = getClosestHandle( From 491a1dde90475efdd7b0e80b9e520baa80ce7743 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Wed, 13 Mar 2024 16:53:32 +0100 Subject: [PATCH 05/14] export EdgeLabel component --- packages/svelte/src/lib/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/svelte/src/lib/index.ts b/packages/svelte/src/lib/index.ts index 589a2910..04548aa9 100644 --- a/packages/svelte/src/lib/index.ts +++ b/packages/svelte/src/lib/index.ts @@ -10,6 +10,7 @@ export * from '$lib/components/ViewportPortal'; export * from '$lib/components/BaseEdge'; export { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '$lib/components/edges'; export * from '$lib/components/Handle'; +export * from '$lib/components/EdgeLabel'; // plugins export * from '$lib/plugins/Controls'; From 2b28a910143df15c5695498d1de57f1ec660ae81 Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 13 Mar 2024 20:31:42 +0100 Subject: [PATCH 06/14] chore(readme): update package info --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 722fa001..df474f4f 100644 --- a/README.md +++ b/README.md @@ -17,20 +17,20 @@ Powerful open source libraries for building node-based UIs with React or Svelte. ## The xyflow mono repo The xyflow repository is the home of four packages: -* React Flow v11 `reactflow` [v11 branch](https://github.com/xyflow/xyflow/tree/v11) -* React Flow v12 (not published yet) [packages/react](./packages/react) +* React Flow 11 `reactflow` [v11 branch](https://github.com/xyflow/xyflow/tree/v11) +* React Flow 12 (beta) `@xyflow/svelte` [packages/react](./packages/react) * Svelte Flow `@xyflow/svelte` [packages/svelte](./packages/svelte) * Shared helper library `@xyflow/system` [packages/system](./packages/system) **We just moved repositories** from the @wbkd org to this one. React Flow v11 will remain on the [v11 branch](https://github.com/xyflow/xyflow/tree/v11). When we have a stable v12, the package name of React Flow will change from `reactflow` to `@xyflow/react`. -## Commercial Usage +## Commercial usage **Are you using React Flow or Svelte Flow for a personal project?** Great! No sponsorship needed, you can support us by reporting any bugs you find, sending us screenshots of your projects, and starring us on Github 🌟 **Are you using React Flow or Svelte Flow at your organization and making money from it?** Awesome! We rely on your support to keep our libraries developed and maintained under an MIT License, just how we like it. For React Flow you can do that on the [React Flow Pro website](https://reactflow.dev/pro) and for both of our libraries you can do it through [Github Sponsors](https://github.com/sponsors/xyflow). -## Getting Started +## Getting started The best way to get started is to check out the [React Flow](https://reactflow.dev/learn) or [Svelte Flow](https://svelteflow.dev/learn) learn section. However if you want to get a sneak peek of how to install the and use the libraries you can see it here: From 0e086956e2a28cc51d924d8d362476aac70846cb Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 14 Mar 2024 13:02:34 +0100 Subject: [PATCH 07/14] chore(shallowNodeData): cleanup --- packages/react/CHANGELOG.md | 6 ++++++ packages/system/src/utils/shallow-node-data.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 0bc7f603..faaa0a57 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,11 @@ # @xyflow/react +## 12.0.0-next.12 + +## Patch changes + +- fix useNodesData: handle invalid node id thanks @saswatax + ## 12.0.0-next.11 ## Patch changes diff --git a/packages/system/src/utils/shallow-node-data.ts b/packages/system/src/utils/shallow-node-data.ts index 42af404e..6bf132b6 100644 --- a/packages/system/src/utils/shallow-node-data.ts +++ b/packages/system/src/utils/shallow-node-data.ts @@ -2,7 +2,7 @@ import { NodeBase } from '../types'; type NodeData = Pick; -export function shallowNodeData(a: NodeData | null | NodeData[], b: NodeData | null | NodeData[]) { +export function shallowNodeData(a: NodeData | NodeData[] | null, b: NodeData | NodeData[] | null) { if (a === null || b === null) { return false; } From cefdc4eca2bf4dd97357db1796634d4afc3cedf8 Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 14 Mar 2024 14:48:53 +0100 Subject: [PATCH 08/14] chore(svelte/handle): cleanup --- packages/svelte/CHANGELOG.md | 6 ++++++ .../svelte/src/lib/components/Handle/Handle.svelte | 11 ++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f784ff70..dd0f28de 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # @xyflow/svelte +## 0.0.39 + +## Patch changes + +- add "connectionindicator" class for `Handle` component + ## 0.0.38 ## ⚠️ Breaking changes diff --git a/packages/svelte/src/lib/components/Handle/Handle.svelte b/packages/svelte/src/lib/components/Handle/Handle.svelte index a492d0c0..f77f6413 100644 --- a/packages/svelte/src/lib/components/Handle/Handle.svelte +++ b/packages/svelte/src/lib/components/Handle/Handle.svelte @@ -24,6 +24,7 @@ export let isConnectable: $$Props['isConnectable'] = undefined; export let onconnect: $$Props['onconnect'] = undefined; export let ondisconnect: $$Props['ondisconnect'] = undefined; + // @todo implement connectablestart, connectableend // export let isConnectableStart: $$Props['isConnectableStart'] = undefined; // export let isConnectableEnd: $$Props['isConnectableEnd'] = undefined; @@ -140,8 +141,6 @@ : nodeId !== $connection.startHandle?.nodeId || handleId !== $connection.startHandle?.handleId; $: valid = connectingTo && $connection.status === 'valid'; - - // @todo implement connectablestart, connectableend