diff --git a/examples/react/cypress/components/reactflow/basic-props.cy.tsx b/examples/react/cypress/components/reactflow/basic-props.cy.tsx
index c8ac818a..a0738eff 100644
--- a/examples/react/cypress/components/reactflow/basic-props.cy.tsx
+++ b/examples/react/cypress/components/reactflow/basic-props.cy.tsx
@@ -6,7 +6,7 @@ import * as simpleflow from '../../fixtures/simpleflow';
describe(': Basic Props', () => {
describe('uses defaultNodes and defaultEdges', () => {
beforeEach(() => {
- cy.mount();
+ cy.mount();
});
it('mounts nodes and edges', () => {
diff --git a/examples/react/cypress/components/reactflow/on-nodes-change.cy.tsx b/examples/react/cypress/components/reactflow/on-nodes-change.cy.tsx
new file mode 100644
index 00000000..a15fc67e
--- /dev/null
+++ b/examples/react/cypress/components/reactflow/on-nodes-change.cy.tsx
@@ -0,0 +1,138 @@
+import { useCallback, useState } from 'react';
+import { OnNodesChange, Panel, ReactFlow, ReactFlowProps, applyNodeChanges, useReactFlow, Node } from '@xyflow/react';
+
+const nodes = [
+ {
+ id: '1',
+ data: { label: 'Node 1' },
+ position: { x: 0, y: 0 },
+ style: { width: 100, height: 40 },
+ },
+ {
+ id: '2',
+ data: { label: 'Node 2' },
+ position: { x: 200, y: 200 },
+ style: { width: 80, height: 50 },
+ },
+];
+
+describe(': onNodesChange', () => {
+ beforeEach(() => {
+ const onNodesChange = cy.spy().as('onNodesChange');
+ cy.mount().wait(500);
+ });
+
+ it('receive initial dimension change', () => {
+ const expectedChanges = nodes.map((n) => ({
+ type: 'dimensions',
+ id: n.id,
+ dimensions: n.style,
+ }));
+
+ cy.get('@onNodesChange').should('have.callCount', 1);
+ cy.get('@onNodesChange').should('have.calledWith', expectedChanges);
+ });
+
+ it('receive replace and dimension change', () => {
+ const expectedDimensionChanges = [
+ {
+ type: 'dimensions',
+ id: '1',
+ dimensions: { width: 200, height: 100 },
+ },
+ ];
+
+ const expectedReplaceChanges = [
+ {
+ type: 'replace',
+ id: '1',
+ item: {
+ ...nodes[0],
+ computed: { positionAbsolute: nodes[0].position, width: 200, height: 100 },
+ style: { width: 200, height: 100 },
+ },
+ },
+ ];
+
+ cy.get('[data-id=update-btn]').click();
+
+ cy.get('@onNodesChange').should('have.callCount', 3);
+ cy.get('@onNodesChange').then((s) => {
+ // dimension change (already checked above)
+ // const firstCall = s.getCall(0);
+ // replace change
+ // @ts-ignore
+ const secondChangeCallArgs = s.getCall(1).args[0];
+ // dimension change
+ // @ts-ignore
+ const thirdChangeCallArgs = s.getCall(2).args[0];
+
+ expect(secondChangeCallArgs).to.be.deep.eq(expectedReplaceChanges);
+ expect(thirdChangeCallArgs).to.be.deep.eq(expectedDimensionChanges);
+ });
+ });
+
+ it('receive select and unselect change', () => {
+ const expectedSelectChanges = [{ id: '1', type: 'select', selected: true }];
+ const expectedUnselectChanges = [{ id: '1', type: 'select', selected: false }];
+
+ cy.get('.react-flow__node').first().click();
+ cy.get('@onNodesChange').should('have.calledWith', expectedSelectChanges);
+
+ cy.get('.react-flow__pane').first().click({ force: true });
+ cy.get('@onNodesChange').should('have.calledWith', expectedUnselectChanges);
+ });
+
+ it('receive position change', () => {
+ const endPosition = { x: 200, y: 25 };
+ const expectedChanges = [
+ { id: '1', type: 'position', dragging: false, position: endPosition, positionAbsolute: endPosition },
+ ];
+
+ cy.drag('.react-flow__node:first', endPosition).then(() => {
+ cy.get('@onNodesChange').should('have.calledWith', expectedChanges);
+ });
+ });
+
+ it('receive remove change', () => {
+ const expectedChanges = [{ id: '1', type: 'remove' }];
+
+ cy.get('.react-flow__node').first().click();
+ cy.realPress('Backspace');
+
+ cy.get('@onNodesChange').should('have.calledWith', expectedChanges);
+ });
+});
+
+// test specific helpers
+
+function UpdateButton() {
+ const { updateNode } = useReactFlow();
+ const updateNodeDimensions = () => {
+ updateNode('1', { style: { width: 200, height: 100 } });
+ };
+
+ return (
+
+
+
+ );
+}
+
+type CompProps = ReactFlowProps & { onNodesChange: (changes: any) => void };
+
+function Comp(props: CompProps) {
+ const [nodes, setNodes] = useState(props.nodes || []);
+ const onNodesChange: OnNodesChange = useCallback((changes) => {
+ props.onNodesChange(changes);
+ setNodes((nds) => applyNodeChanges(changes, nds));
+ }, []);
+
+ return (
+
+
+
+ );
+}
diff --git a/examples/react/cypress/e2e/basic.cy.ts b/examples/react/cypress/e2e/basic.cy.ts
index 8328a307..10c0ce62 100644
--- a/examples/react/cypress/e2e/basic.cy.ts
+++ b/examples/react/cypress/e2e/basic.cy.ts
@@ -49,21 +49,16 @@ describe('Basic Flow Rendering', { testIsolation: false }, () => {
it('selects one node with a selection', () => {
cy.get('body')
- .type('{shift}', { release: false })
- .wait(50)
+ .type('{Shift}', { release: false })
.get('.react-flow__pane')
.trigger('mousedown', 1, 10, { button: 0, force: true })
- .trigger('mousemove', 1000, 450, { button: 0 })
- .wait(50)
- .trigger('mouseup', 1000, 450, { button: 0, force: true });
+ .trigger('mousemove', 1000, 200, { button: 0 })
+ .trigger('mouseup', 1000, 200, { button: 0 });
- cy.wait(200);
-
- cy.get('.react-flow__node').eq(1).should('have.class', 'selected');
+ cy.get('.react-flow__node').eq(0).should('have.class', 'selected');
cy.get('.react-flow__node').eq(3).should('have.not.class', 'selected');
cy.get('.react-flow__nodesselection-rect');
-
cy.get('body').type('{shift}', { release: true, force: true });
});
@@ -97,19 +92,19 @@ describe('Basic Flow Rendering', { testIsolation: false }, () => {
it('drags a node', () => {
const styleBeforeDrag = Cypress.$('.react-flow__node:first').css('transform');
- cy.drag('.react-flow__node:first', { x: 500, y: 25 }).then(($el: any) => {
+ cy.drag('.react-flow__node:first', { x: 10, y: 10 }).then(($el: any) => {
const styleAfterDrag = $el.css('transform');
expect(styleBeforeDrag).to.not.equal(styleAfterDrag);
});
});
- // @TODO: why does this fail since react18?
- // it('removes a node', () => {
- // cy.get('.react-flow__node').contains('Node 1').click().should('have.class', 'selected');
- // cy.get('html').type('{backspace}').wait(100);
- // cy.get('.react-flow__node').should('have.length', 3);
- // cy.get('.react-flow__edge').should('have.length', 1);
- // });
+ it('removes a node', () => {
+ cy.get('.react-flow__node').contains('Node 1').click().should('have.class', 'selected');
+ cy.get('html').realPress('Backspace');
+
+ cy.get('.react-flow__node').should('have.length', 3);
+ cy.get('.react-flow__edge').should('have.length', 0);
+ });
it('connects nodes', () => {
cy.get('.react-flow__node')
@@ -125,16 +120,15 @@ describe('Basic Flow Rendering', { testIsolation: false }, () => {
.trigger('mouseup', { force: true, button: 0 });
cy.get('.react-flow__edge').as('edge');
- cy.get('@edge').should('have.length', 3);
+ cy.get('@edge').should('have.length', 1);
});
- // @TODO: why does this fail since react18?
- // it('removes an edge', () => {
- // cy.get('.react-flow__edge:first').click();
- // cy.get('body').type('{backspace}');
+ it('removes an edge', () => {
+ cy.get('.react-flow__edge:first').click();
+ cy.get('html').realPress('Backspace');
- // cy.get('.react-flow__edge').should('have.length', 1);
- // });
+ cy.get('.react-flow__edge').should('have.length', 0);
+ });
it('drags the pane', () => {
const styleBeforeDrag = Cypress.$('.react-flow__viewport').css('transform');
diff --git a/examples/react/cypress/support/ControlledFlow.tsx b/examples/react/cypress/support/ControlledFlow.tsx
index eaa560bf..8474b81e 100644
--- a/examples/react/cypress/support/ControlledFlow.tsx
+++ b/examples/react/cypress/support/ControlledFlow.tsx
@@ -59,7 +59,7 @@ function ControlledFlow({
handlers.onConnect = onConnect;
}
- return ;
+ return ;
}
export default ControlledFlow;
diff --git a/examples/react/cypress/support/commands.ts b/examples/react/cypress/support/commands.ts
index e138e4cf..3a350dc5 100644
--- a/examples/react/cypress/support/commands.ts
+++ b/examples/react/cypress/support/commands.ts
@@ -9,7 +9,7 @@ Cypress.Commands.add('drag', (selector, { x, y }) =>
const nextY: number = centerY + y;
return elementToDrag
- .trigger('mousedown', { view: window })
+ .trigger('mousedown', { view: window, force: true })
.trigger('mousemove', nextX, nextY, { force: true })
.wait(50)
.trigger('mouseup', { view: window, force: true });
diff --git a/examples/react/cypress/support/e2e.ts b/examples/react/cypress/support/e2e.ts
index d153dd3e..29be4a69 100644
--- a/examples/react/cypress/support/e2e.ts
+++ b/examples/react/cypress/support/e2e.ts
@@ -15,6 +15,7 @@
// Import commands.js using ES2015 syntax:
import './commands';
+import 'cypress-real-events/support';
const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/;
diff --git a/examples/react/package.json b/examples/react/package.json
index 36dd09a6..24bbee4a 100644
--- a/examples/react/package.json
+++ b/examples/react/package.json
@@ -30,8 +30,8 @@
"@types/react-dom": "^18.2.14",
"@vitejs/plugin-react": "4.1.1",
"@vitejs/plugin-react-swc": "^3.4.1",
- "cypress": "12.17.3",
- "cypress-real-events": "1.11.0",
+ "cypress": "13.6.6",
+ "cypress-real-events": "1.12.0",
"start-server-and-test": "^2.0.2",
"typescript": "5.2.2",
"vite": "4.5.0"
diff --git a/examples/react/src/examples/Basic/index.tsx b/examples/react/src/examples/Basic/index.tsx
index a41f50db..cafa9fa8 100644
--- a/examples/react/src/examples/Basic/index.tsx
+++ b/examples/react/src/examples/Basic/index.tsx
@@ -45,7 +45,6 @@ const initialNodes: Node[] = [
data: { label: 'Node 4' },
position: { x: 400, y: 200 },
className: 'light',
- connectable: false,
},
];
@@ -129,6 +128,7 @@ const BasicFlow = () => {
selectNodesOnDrag={false}
elevateEdgesOnSelect
elevateNodesOnSelect={false}
+ nodeDragThreshold={0}
>
diff --git a/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx b/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx
index 72255e27..1265d408 100644
--- a/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx
+++ b/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx
@@ -1,4 +1,4 @@
-import React, { memo, FC, CSSProperties, useCallback } from 'react';
+import React, { memo, CSSProperties, useCallback } from 'react';
import { Handle, Position, NodeProps, Connection, Edge, useOnViewportChange, Viewport } from '@xyflow/react';
import type { ColorSelectorNode } from '.';
@@ -13,7 +13,7 @@ const sourceHandleStyleB: CSSProperties = {
const onConnect = (params: Connection | Edge) => console.log('handle onConnect', params);
-const ColorSelectorNode: FC> = ({ data, isConnectable }) => {
+function ColorSelectorNode({ data, isConnectable }: NodeProps) {
const onStart = useCallback((viewport: Viewport) => console.log('onStart', viewport), []);
const onChange = useCallback((viewport: Viewport) => console.log('onChange', viewport), []);
const onEnd = useCallback((viewport: Viewport) => console.log('onEnd', viewport), []);
@@ -44,6 +44,6 @@ const ColorSelectorNode: FC> = ({ data, isC
>
);
-};
+}
export default memo(ColorSelectorNode);
diff --git a/examples/react/src/examples/CustomNode/index.tsx b/examples/react/src/examples/CustomNode/index.tsx
index 16a99803..03934e40 100644
--- a/examples/react/src/examples/CustomNode/index.tsx
+++ b/examples/react/src/examples/CustomNode/index.tsx
@@ -1,4 +1,4 @@
-import { useState, useEffect, MouseEvent, ChangeEvent, useCallback } from 'react';
+import { useState, useEffect, MouseEvent, ChangeEvent, useCallback, useRef } from 'react';
import {
ReactFlow,
MiniMap,
@@ -17,6 +17,8 @@ import {
OnBeforeDelete,
BuiltInNode,
BuiltInEdge,
+ NodeTypes,
+ ReactFlowProvider,
} from '@xyflow/react';
import ColorSelectorNode from './ColorSelectorNode';
@@ -40,11 +42,12 @@ const initBgColor = '#1A192B';
const connectionLineStyle = { stroke: '#fff' };
const snapGrid: SnapGrid = [16, 16];
-const nodeTypes = {
+const nodeTypes: NodeTypes = {
selectorNode: ColorSelectorNode,
};
const CustomNodeFlow = () => {
+ const ref = useRef(null);
const [nodes, setNodes] = useState([]);
const onNodesChange: OnNodesChange = useCallback(
(changes) =>
@@ -165,6 +168,7 @@ const CustomNodeFlow = () => {
minZoom={0.3}
maxZoom={2}
onBeforeDelete={onBeforeDelete}
+ ref={ref}
>
nodeStrokeColor={(n: MyNode): string => {
@@ -186,4 +190,8 @@ const CustomNodeFlow = () => {
);
};
-export default CustomNodeFlow;
+export default () => (
+
+
+
+);
diff --git a/examples/react/src/examples/DragHandle/index.tsx b/examples/react/src/examples/DragHandle/index.tsx
index a7a75fcf..2f7b9502 100644
--- a/examples/react/src/examples/DragHandle/index.tsx
+++ b/examples/react/src/examples/DragHandle/index.tsx
@@ -33,6 +33,7 @@ const DragHandleFlow = () => {
edges={edges}
nodeTypes={nodeTypes}
onNodeClick={onNodeClick}
+ nodeDragThreshold={0}
/>
);
};
diff --git a/examples/react/src/examples/Interaction/index.tsx b/examples/react/src/examples/Interaction/index.tsx
index 0dcee27b..ba3ecf03 100644
--- a/examples/react/src/examples/Interaction/index.tsx
+++ b/examples/react/src/examples/Interaction/index.tsx
@@ -36,7 +36,7 @@ const onNodeClick = (_: ReactMouseEvent, node: Node) => console.log('click', nod
const onEdgeClick = (_: ReactMouseEvent, edge: Edge) => console.log('click', edge);
const onPaneClick = (event: ReactMouseEvent) => console.log('onPaneClick', event);
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event);
-const onPaneContextMenu = (event: ReactMouseEvent) => console.log('onPaneContextMenu', event);
+const onPaneContextMenu = (event: ReactMouseEvent | MouseEvent) => console.log('onPaneContextMenu', event);
const onMoveEnd = (_: TouchEvent | MouseEvent | null, viewport: Viewport) => console.log('onMoveEnd', viewport);
const InteractionFlow = () => {
@@ -80,6 +80,7 @@ const InteractionFlow = () => {
onPaneClick={captureZoomClick ? onPaneClick : undefined}
onPaneScroll={captureZoomScroll ? onPaneScroll : undefined}
onPaneContextMenu={captureZoomClick ? onPaneContextMenu : undefined}
+ nodeDragThreshold={0}
onMoveEnd={onMoveEnd}
>
diff --git a/examples/react/src/examples/UseNodesData/index.tsx b/examples/react/src/examples/UseNodesData/index.tsx
index 91cc1f94..dc0b43dd 100644
--- a/examples/react/src/examples/UseNodesData/index.tsx
+++ b/examples/react/src/examples/UseNodesData/index.tsx
@@ -20,8 +20,8 @@ export type ResultNode = Node<{}, 'result'>;
export type UppercaseNode = Node<{ text: string }, 'uppercase'>;
export type MyNode = TextNode | ResultNode | UppercaseNode;
-export function isTextNode(node: any): node is TextNode {
- return node.type === 'text';
+export function isTextNode(node: any): node is TextNode | UppercaseNode {
+ return node.type === 'text' || node.type === 'uppercase';
}
const nodeTypes = {
diff --git a/examples/react/src/examples/UseReactFlow/index.tsx b/examples/react/src/examples/UseReactFlow/index.tsx
index bed127fb..2177d0cc 100644
--- a/examples/react/src/examples/UseReactFlow/index.tsx
+++ b/examples/react/src/examples/UseReactFlow/index.tsx
@@ -27,18 +27,21 @@ const initialNodes: Node[] = [
data: { label: 'Node 2' },
position: { x: 100, y: 100 },
className: 'light',
+ type: 'default',
},
{
id: '3',
data: { label: 'Node 3' },
position: { x: 400, y: 100 },
className: 'light',
+ type: 'default',
},
{
id: '4',
data: { label: 'Node 4' },
position: { x: 400, y: 200 },
className: 'light',
+ type: 'default',
},
];
@@ -85,6 +88,7 @@ const UseZoomPanHelperFlow = () => {
data: {
label: `${projectedPosition.x}-${projectedPosition.y}`,
},
+ type: 'default',
})
);
},
@@ -106,6 +110,7 @@ const UseZoomPanHelperFlow = () => {
data: {
label: 'New Node',
},
+ type: 'default',
};
addNodes(newNode);
@@ -139,8 +144,8 @@ const UseZoomPanHelperFlow = () => {
const onSetNodes = () => {
setNodes([
- { id: 'a', position: { x: 0, y: 0 }, data: { label: 'Node a' } },
- { id: 'b', position: { x: 0, y: 150 }, data: { label: 'Node b' } },
+ { id: 'a', type: 'default', position: { x: 0, y: 0 }, data: { label: 'Node a' } },
+ { id: 'b', type: 'default', position: { x: 0, y: 150 }, data: { label: 'Node b' } },
]);
setEdges([{ id: 'a-b', source: 'a', target: 'b' }]);
diff --git a/examples/react/src/generic-tests/edges/general.ts b/examples/react/src/generic-tests/edges/general.ts
index bb768b09..23977657 100644
--- a/examples/react/src/generic-tests/edges/general.ts
+++ b/examples/react/src/generic-tests/edges/general.ts
@@ -3,6 +3,7 @@ import { MarkerType } from '@xyflow/react';
export default {
flowProps: {
fitView: true,
+ multiSelectionKeyCode: 's',
nodes: [
{
id: '1',
diff --git a/examples/react/tsconfig.json b/examples/react/tsconfig.json
index 7156a499..0519e0a8 100644
--- a/examples/react/tsconfig.json
+++ b/examples/react/tsconfig.json
@@ -15,7 +15,7 @@
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
- "types": ["cypress", "cypress-real-events"]
+ "types": ["cypress", "node", "cypress-real-events"]
},
"include": ["**/*.ts", "**/*.tsx"],
"references": [{ "path": "./tsconfig.node.json" }]
diff --git a/examples/svelte/package.json b/examples/svelte/package.json
index 2679983a..edf69c6a 100644
--- a/examples/svelte/package.json
+++ b/examples/svelte/package.json
@@ -12,8 +12,8 @@
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
- "@sveltejs/adapter-auto": "^2.1.1",
- "@sveltejs/kit": "^1.27.3",
+ "@sveltejs/adapter-auto": "^3.1.1",
+ "@sveltejs/kit": "^2.5.2",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"eslint": "^8.53.0",
@@ -21,8 +21,8 @@
"eslint-plugin-svelte": "^2.35.0",
"prettier": "^3.0.3",
"prettier-plugin-svelte": "^3.0.3",
- "svelte": "^4.2.2",
- "svelte-check": "^3.5.2",
+ "svelte": "^4.2.12",
+ "svelte-check": "^3.6.6",
"tslib": "^2.6.2",
"typescript": "^5.2.2",
"vite": "^4.5.0"
@@ -30,6 +30,7 @@
"type": "module",
"dependencies": {
"@dagrejs/dagre": "^1.0.4",
+ "@sveltejs/vite-plugin-svelte": "^3.0.2",
"@xyflow/svelte": "workspace:^"
}
}
diff --git a/examples/svelte/src/routes/examples/customnode/+page.svelte b/examples/svelte/src/routes/examples/customnode/+page.svelte
index 525b0706..f89210c8 100644
--- a/examples/svelte/src/routes/examples/customnode/+page.svelte
+++ b/examples/svelte/src/routes/examples/customnode/+page.svelte
@@ -1,5 +1,4 @@
diff --git a/examples/svelte/src/routes/examples/usenodesdata/+page.svelte b/examples/svelte/src/routes/examples/usenodesdata/+page.svelte
index fc85ee26..91fd8432 100644
--- a/examples/svelte/src/routes/examples/usenodesdata/+page.svelte
+++ b/examples/svelte/src/routes/examples/usenodesdata/+page.svelte
@@ -5,8 +5,8 @@
type UppercaseNodeType = Node<{ text: string }, 'uppercase'>;
type ResultNodeType = Node<{}, 'result'>;
- export function isTextNode(node: any): node is TextNodeType {
- return node.type === 'text';
+ export function isTextNode(node: any): node is TextNodeType | UppercaseNode {
+ return node.type === 'text' || node.type === 'uppercase';
}
export type MyNode = TextNodeType | UppercaseNodeType | ResultNodeType;
diff --git a/examples/svelte/svelte.config.js b/examples/svelte/svelte.config.js
index 80719d79..56ed51a8 100644
--- a/examples/svelte/svelte.config.js
+++ b/examples/svelte/svelte.config.js
@@ -1,5 +1,5 @@
import adapter from '@sveltejs/adapter-auto';
-import { vitePreprocess } from '@sveltejs/kit/vite';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md
index 051fdbd9..0bc7f603 100644
--- a/packages/react/CHANGELOG.md
+++ b/packages/react/CHANGELOG.md
@@ -1,5 +1,15 @@
# @xyflow/react
+## 12.0.0-next.11
+
+## Patch changes
+
+- fix `ref` prop for `ReactFlow` and `Handle` component
+- unify `Edge` and `Node` type handling
+- fix safari: prevent selection of viewport
+- fix `useNodesData` hook to prevent re-renderings
+- fix edges: allow start at 0,0
+
## 12.0.0-next.10
## ⚠️ Breaking changes
diff --git a/packages/react/package.json b/packages/react/package.json
index d9105c4f..542a7071 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -1,6 +1,6 @@
{
"name": "@xyflow/react",
- "version": "12.0.0-next.10",
+ "version": "12.0.0-next.11",
"description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.",
"keywords": [
"react",
diff --git a/packages/react/src/components/EdgeWrapper/index.tsx b/packages/react/src/components/EdgeWrapper/index.tsx
index 3baea708..c114c1b4 100644
--- a/packages/react/src/components/EdgeWrapper/index.tsx
+++ b/packages/react/src/components/EdgeWrapper/index.tsx
@@ -111,7 +111,7 @@ export function EdgeWrapper({
[edge.markerEnd, rfId]
);
- if (edge.hidden || !sourceX || !sourceY || !targetX || !targetY) {
+ if (edge.hidden || sourceX === null || sourceY === null || targetX === null || targetY === null) {
return null;
}
@@ -212,6 +212,7 @@ export function EdgeWrapper({
id={id}
source={edge.source}
target={edge.target}
+ type={edge.type}
selected={edge.selected}
animated={edge.animated}
label={edge.label}
diff --git a/packages/react/src/components/EdgeWrapper/utils.ts b/packages/react/src/components/EdgeWrapper/utils.ts
index 0ac2a2e8..afbccb13 100644
--- a/packages/react/src/components/EdgeWrapper/utils.ts
+++ b/packages/react/src/components/EdgeWrapper/utils.ts
@@ -1,5 +1,4 @@
-import type { ComponentType } from 'react';
-import type { EdgeProps, EdgeTypes } from '../../types';
+import type { EdgeTypes } from '../../types';
import {
BezierEdgeInternal,
StraightEdgeInternal,
@@ -9,11 +8,11 @@ import {
} from '../Edges';
export const builtinEdgeTypes: EdgeTypes = {
- default: BezierEdgeInternal as ComponentType,
- straight: StraightEdgeInternal as ComponentType,
- step: StepEdgeInternal as ComponentType,
- smoothstep: SmoothStepEdgeInternal as ComponentType,
- simplebezier: SimpleBezierEdgeInternal as ComponentType,
+ default: BezierEdgeInternal,
+ straight: StraightEdgeInternal,
+ step: StepEdgeInternal,
+ smoothstep: SmoothStepEdgeInternal,
+ simplebezier: SimpleBezierEdgeInternal,
};
export const nullPosition = {
diff --git a/packages/react/src/components/Handle/index.tsx b/packages/react/src/components/Handle/index.tsx
index 75c36b8c..f1f84cce 100644
--- a/packages/react/src/components/Handle/index.tsx
+++ b/packages/react/src/components/Handle/index.tsx
@@ -1,9 +1,11 @@
-/*
- * The Handle component is used to connect nodes. When the user mousedowns a handle, we start the connection process.
- * The user can then drag the connection to another handle or node. When the user releases the mouse, we check if the
- * connection is valid and if so, we call the onConnect callback.
- */
-import { memo, HTMLAttributes, forwardRef, MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
+import {
+ type HTMLAttributes,
+ type MouseEvent as ReactMouseEvent,
+ type TouchEvent as ReactTouchEvent,
+ type ForwardedRef,
+ memo,
+ forwardRef,
+} from 'react';
import cc from 'classcat';
import { shallow } from 'zustand/shallow';
import {
@@ -58,190 +60,186 @@ const connectingSelector =
};
};
-const HandleComponent = forwardRef(
- (
- {
- type = 'source',
- position = Position.Top,
- isValidConnection,
- isConnectable = true,
- isConnectableStart = true,
- isConnectableEnd = true,
- id,
- onConnect,
- children,
- className,
- onMouseDown,
- onTouchStart,
- ...rest
- },
- ref
- ) => {
- const handleId = id || null;
- const isTarget = type === 'target';
- const store = useStoreApi();
- const nodeId = useNodeId();
- const { connectOnClick, noPanClassName, rfId } = useStore(selector, shallow);
- const { connectingFrom, connectingTo, clickConnecting, isPossibleEndHandle, connectionInProcess, valid } = useStore(
- connectingSelector(nodeId, handleId, type),
- shallow
- );
+function HandleComponent(
+ {
+ type = 'source',
+ position = Position.Top,
+ isValidConnection,
+ isConnectable = true,
+ isConnectableStart = true,
+ isConnectableEnd = true,
+ id,
+ onConnect,
+ children,
+ className,
+ onMouseDown,
+ onTouchStart,
+ ...rest
+ }: HandleComponentProps,
+ ref: ForwardedRef
+) {
+ const handleId = id || null;
+ const isTarget = type === 'target';
+ const store = useStoreApi();
+ const nodeId = useNodeId();
+ const { connectOnClick, noPanClassName, rfId } = useStore(selector, shallow);
+ const { connectingFrom, connectingTo, clickConnecting, isPossibleEndHandle, connectionInProcess, valid } = useStore(
+ connectingSelector(nodeId, handleId, type),
+ shallow
+ );
- if (!nodeId) {
- store.getState().onError?.('010', errorMessages['error010']());
+ if (!nodeId) {
+ store.getState().onError?.('010', errorMessages['error010']());
+ }
+
+ const onConnectExtended = (params: Connection) => {
+ const { defaultEdgeOptions, onConnect: onConnectAction, hasDefaultEdges } = store.getState();
+
+ const edgeParams = {
+ ...defaultEdgeOptions,
+ ...params,
+ };
+ if (hasDefaultEdges) {
+ const { edges, setEdges } = store.getState();
+ setEdges(addEdge(edgeParams, edges));
}
- const onConnectExtended = (params: Connection) => {
- const { defaultEdgeOptions, onConnect: onConnectAction, hasDefaultEdges } = store.getState();
+ onConnectAction?.(edgeParams);
+ onConnect?.(edgeParams);
+ };
- const edgeParams = {
- ...defaultEdgeOptions,
- ...params,
- };
- if (hasDefaultEdges) {
- const { edges, setEdges } = store.getState();
- setEdges(addEdge(edgeParams, edges));
- }
+ const onPointerDown = (event: ReactMouseEvent | ReactTouchEvent) => {
+ if (!nodeId) {
+ return;
+ }
- onConnectAction?.(edgeParams);
- onConnect?.(edgeParams);
- };
+ const isMouseTriggered = isMouseEvent(event.nativeEvent);
- const onPointerDown = (event: ReactMouseEvent | ReactTouchEvent) => {
- if (!nodeId) {
- return;
- }
+ if (
+ isConnectableStart &&
+ ((isMouseTriggered && (event as ReactMouseEvent).button === 0) || !isMouseTriggered)
+ ) {
+ const currentStore = store.getState();
- const isMouseTriggered = isMouseEvent(event.nativeEvent);
-
- if (
- isConnectableStart &&
- ((isMouseTriggered && (event as ReactMouseEvent).button === 0) || !isMouseTriggered)
- ) {
- const currentStore = store.getState();
-
- XYHandle.onPointerDown(event.nativeEvent, {
- autoPanOnConnect: currentStore.autoPanOnConnect,
- connectionMode: currentStore.connectionMode,
- connectionRadius: currentStore.connectionRadius,
- domNode: currentStore.domNode,
- nodes: currentStore.nodes,
- lib: currentStore.lib,
- isTarget,
- handleId,
- nodeId,
- flowId: currentStore.rfId,
- panBy: currentStore.panBy,
- cancelConnection: currentStore.cancelConnection,
- onConnectStart: currentStore.onConnectStart,
- onConnectEnd: currentStore.onConnectEnd,
- updateConnection: currentStore.updateConnection,
- onConnect: onConnectExtended,
- isValidConnection: isValidConnection || currentStore.isValidConnection,
- getTransform: () => store.getState().transform,
- });
- }
-
- if (isMouseTriggered) {
- onMouseDown?.(event as ReactMouseEvent);
- } else {
- onTouchStart?.(event as ReactTouchEvent);
- }
- };
-
- const onClick = (event: ReactMouseEvent) => {
- const {
- onClickConnectStart,
- onClickConnectEnd,
- connectionClickStartHandle,
- connectionMode,
- isValidConnection: isValidConnectionStore,
- lib,
- rfId: flowId,
- } = store.getState();
-
- if (!nodeId || (!connectionClickStartHandle && !isConnectableStart)) {
- return;
- }
-
- if (!connectionClickStartHandle) {
- onClickConnectStart?.(event.nativeEvent, { nodeId, handleId, handleType: type });
- store.setState({ connectionClickStartHandle: { nodeId, type, handleId } });
- return;
- }
-
- const doc = getHostForElement(event.target as HTMLElement);
- const isValidConnectionHandler = isValidConnection || isValidConnectionStore;
- const { connection, isValid } = XYHandle.isValid(event.nativeEvent, {
- handle: {
- nodeId,
- id: handleId,
- type,
- },
- connectionMode,
- fromNodeId: connectionClickStartHandle.nodeId,
- fromHandleId: connectionClickStartHandle.handleId || null,
- fromType: connectionClickStartHandle.type,
- isValidConnection: isValidConnectionHandler,
- flowId,
- doc,
- lib,
+ XYHandle.onPointerDown(event.nativeEvent, {
+ autoPanOnConnect: currentStore.autoPanOnConnect,
+ connectionMode: currentStore.connectionMode,
+ connectionRadius: currentStore.connectionRadius,
+ domNode: currentStore.domNode,
+ nodes: currentStore.nodes,
+ lib: currentStore.lib,
+ isTarget,
+ handleId,
+ nodeId,
+ flowId: currentStore.rfId,
+ panBy: currentStore.panBy,
+ cancelConnection: currentStore.cancelConnection,
+ onConnectStart: currentStore.onConnectStart,
+ onConnectEnd: currentStore.onConnectEnd,
+ updateConnection: currentStore.updateConnection,
+ onConnect: onConnectExtended,
+ isValidConnection: isValidConnection || currentStore.isValidConnection,
+ getTransform: () => store.getState().transform,
});
+ }
- if (isValid && connection) {
- onConnectExtended(connection);
- }
+ if (isMouseTriggered) {
+ onMouseDown?.(event as ReactMouseEvent);
+ } else {
+ onTouchStart?.(event as ReactTouchEvent);
+ }
+ };
- onClickConnectEnd?.(event as unknown as MouseEvent);
+ const onClick = (event: ReactMouseEvent) => {
+ const {
+ onClickConnectStart,
+ onClickConnectEnd,
+ connectionClickStartHandle,
+ connectionMode,
+ isValidConnection: isValidConnectionStore,
+ lib,
+ rfId: flowId,
+ } = store.getState();
- store.setState({ connectionClickStartHandle: null });
- };
+ if (!nodeId || (!connectionClickStartHandle && !isConnectableStart)) {
+ return;
+ }
- return (
-
- {children}
-
- );
- }
-);
+ if (!connectionClickStartHandle) {
+ onClickConnectStart?.(event.nativeEvent, { nodeId, handleId, handleType: type });
+ store.setState({ connectionClickStartHandle: { nodeId, type, handleId } });
+ return;
+ }
-HandleComponent.displayName = 'Handle';
+ const doc = getHostForElement(event.target as HTMLElement);
+ const isValidConnectionHandler = isValidConnection || isValidConnectionStore;
+ const { connection, isValid } = XYHandle.isValid(event.nativeEvent, {
+ handle: {
+ nodeId,
+ id: handleId,
+ type,
+ },
+ connectionMode,
+ fromNodeId: connectionClickStartHandle.nodeId,
+ fromHandleId: connectionClickStartHandle.handleId || null,
+ fromType: connectionClickStartHandle.type,
+ isValidConnection: isValidConnectionHandler,
+ flowId,
+ doc,
+ lib,
+ });
+
+ if (isValid && connection) {
+ onConnectExtended(connection);
+ }
+
+ onClickConnectEnd?.(event as unknown as MouseEvent);
+
+ store.setState({ connectionClickStartHandle: null });
+ };
+
+ return (
+
+ {children}
+
+ );
+}
/**
- * The Handle component is the part of a node that can be used to connect nodes.
+ * The Handle component is a UI element that is used to connect nodes.
*/
-export const Handle = memo(HandleComponent);
+export const Handle = memo(forwardRef(HandleComponent));
diff --git a/packages/react/src/components/NodeWrapper/utils.tsx b/packages/react/src/components/NodeWrapper/utils.tsx
index f98b6bb6..d6f7cc1a 100644
--- a/packages/react/src/components/NodeWrapper/utils.tsx
+++ b/packages/react/src/components/NodeWrapper/utils.tsx
@@ -1,5 +1,4 @@
-import type { ComponentType } from 'react';
-import type { NodeProps, XYPosition } from '@xyflow/system';
+import type { XYPosition } from '@xyflow/system';
import { InputNode } from '../Nodes/InputNode';
import { DefaultNode } from '../Nodes/DefaultNode';
@@ -15,10 +14,10 @@ export const arrowKeyDiffs: Record = {
};
export const builtinNodeTypes: NodeTypes = {
- input: InputNode as ComponentType,
- default: DefaultNode as ComponentType,
- output: OutputNode as ComponentType,
- group: GroupNode as ComponentType,
+ input: InputNode,
+ default: DefaultNode,
+ output: OutputNode,
+ group: GroupNode,
};
export function getNodeInlineStyleDimensions(
diff --git a/packages/react/src/components/Nodes/DefaultNode.tsx b/packages/react/src/components/Nodes/DefaultNode.tsx
index 132044c5..7bcbae08 100644
--- a/packages/react/src/components/Nodes/DefaultNode.tsx
+++ b/packages/react/src/components/Nodes/DefaultNode.tsx
@@ -1,13 +1,14 @@
-import { Position, type NodeProps } from '@xyflow/system';
+import { Position } from '@xyflow/system';
import { Handle } from '../../components/Handle';
+import type { BuiltInNode, NodeProps } from '../../types/nodes';
export function DefaultNode({
data,
isConnectable,
targetPosition = Position.Top,
sourcePosition = Position.Bottom,
-}: NodeProps) {
+}: NodeProps) {
return (
<>
diff --git a/packages/react/src/components/Nodes/InputNode.tsx b/packages/react/src/components/Nodes/InputNode.tsx
index e4b9da9b..15ded922 100644
--- a/packages/react/src/components/Nodes/InputNode.tsx
+++ b/packages/react/src/components/Nodes/InputNode.tsx
@@ -1,8 +1,9 @@
-import { Position, type NodeProps } from '@xyflow/system';
+import { Position } from '@xyflow/system';
import { Handle } from '../../components/Handle';
+import type { BuiltInNode, NodeProps } from '../../types/nodes';
-export function InputNode({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) {
+export function InputNode({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) {
return (
<>
{data?.label}
diff --git a/packages/react/src/components/Nodes/OutputNode.tsx b/packages/react/src/components/Nodes/OutputNode.tsx
index 64af091c..ed61236c 100644
--- a/packages/react/src/components/Nodes/OutputNode.tsx
+++ b/packages/react/src/components/Nodes/OutputNode.tsx
@@ -1,8 +1,9 @@
-import { Position, type NodeProps } from '@xyflow/system';
+import { Position } from '@xyflow/system';
import { Handle } from '../../components/Handle';
+import type { BuiltInNode, NodeProps } from '../../types/nodes';
-export function OutputNode({ data, isConnectable, targetPosition = Position.Top }: NodeProps) {
+export function OutputNode({ data, isConnectable, targetPosition = Position.Top }: NodeProps) {
return (
<>
diff --git a/packages/react/src/container/GraphView/index.tsx b/packages/react/src/container/GraphView/index.tsx
index 80c25dea..3edc380a 100644
--- a/packages/react/src/container/GraphView/index.tsx
+++ b/packages/react/src/container/GraphView/index.tsx
@@ -172,7 +172,6 @@ function GraphViewComponent
-
nodeTypes={nodeTypes}
onNodeClick={onNodeClick}
@@ -189,6 +188,7 @@ function GraphViewComponent
+
);
diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx
index 414a4745..dfb240d0 100644
--- a/packages/react/src/container/Pane/index.tsx
+++ b/packages/react/src/container/Pane/index.tsx
@@ -128,7 +128,8 @@ export function Pane({
};
const onMouseMove = (event: ReactMouseEvent): void => {
- const { userSelectionRect, edges, transform, nodeOrigin, nodes, onNodesChange, onEdgesChange } = store.getState();
+ const { userSelectionRect, edges, transform, nodeOrigin, nodes, triggerNodeChanges, triggerEdgeChanges } =
+ store.getState();
if (!isSelecting || !containerBounds.current || !userSelectionRect) {
return;
}
@@ -172,17 +173,13 @@ export function Pane({
if (prevSelectedNodesCount.current !== selectedNodeIds.size) {
prevSelectedNodesCount.current = selectedNodeIds.size;
const changes = getSelectionChanges(nodes, selectedNodeIds, true) as NodeChange[];
- if (changes.length) {
- onNodesChange?.(changes);
- }
+ triggerNodeChanges(changes);
}
if (prevSelectedEdgesCount.current !== selectedEdgeIds.size) {
prevSelectedEdgesCount.current = selectedEdgeIds.size;
const changes = getSelectionChanges(edges, selectedEdgeIds) as EdgeChange[];
- if (changes.length) {
- onEdgesChange?.(changes);
- }
+ triggerEdgeChanges(changes);
}
store.setState({
diff --git a/packages/react/src/container/ReactFlow/index.tsx b/packages/react/src/container/ReactFlow/index.tsx
index 88d8843e..eb741684 100644
--- a/packages/react/src/container/ReactFlow/index.tsx
+++ b/packages/react/src/container/ReactFlow/index.tsx
@@ -9,7 +9,7 @@ import { StoreUpdater } from '../../components/StoreUpdater';
import { useColorModeClass } from '../../hooks/useColorModeClass';
import { GraphView } from '../GraphView';
import { Wrapper } from './Wrapper';
-import type { Edge, Node, ReactFlowProps, ReactFlowRefType } from '../../types';
+import type { Edge, Node, ReactFlowProps } from '../../types';
import { defaultViewport as initViewport, defaultNodeOrigin } from './init-values';
const wrapperStyle: CSSProperties = {
@@ -142,7 +142,7 @@ function ReactFlow(
debug,
...rest
}: ReactFlowProps,
- ref: ForwardedRef
+ ref: ForwardedRef
) {
const rfId = id || '1';
const colorModeClassName = useColorModeClass(colorMode);
@@ -286,4 +286,4 @@ function ReactFlow(
);
}
-export default forwardRef(ReactFlow) as typeof ReactFlow;
+export default forwardRef(ReactFlow);
diff --git a/packages/react/src/hooks/useNodesData.ts b/packages/react/src/hooks/useNodesData.ts
index c43a60cc..7472cbf7 100644
--- a/packages/react/src/hooks/useNodesData.ts
+++ b/packages/react/src/hooks/useNodesData.ts
@@ -1,15 +1,9 @@
import { useCallback } from 'react';
-import { shallow } from 'zustand/shallow';
+import { shallowNodeData } from '@xyflow/system';
import { useStore } from '../hooks/useStore';
import type { Node } from '../types';
-export interface NodeDataReturn {
- id: string;
- type: NodeType['type'];
- data: NodeType['data'];
-}
-
/**
* Hook for receiving data of one or multiple nodes
*
@@ -46,7 +40,7 @@ export function useNodesData(nodeIds: any): any {
},
[nodeIds]
),
- shallow
+ shallowNodeData
);
return nodesData;
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index a757ec23..022f2626 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -64,7 +64,6 @@ export {
SelectionMode,
type SelectionRect,
type OnError,
- type NodeProps,
type NodeOrigin,
type OnSelectionDrag,
Position,
diff --git a/packages/react/src/types/component-props.ts b/packages/react/src/types/component-props.ts
index e8d91ba4..6deca270 100644
--- a/packages/react/src/types/component-props.ts
+++ b/packages/react/src/types/component-props.ts
@@ -509,5 +509,3 @@ export interface ReactFlowProps &
* Custom edge component props
* @public
*/
-export type EdgeProps<
- EdgeData extends Record = Record,
- EdgeType extends string | undefined = string | undefined
-> = Pick, 'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'> &
+export type EdgeProps = Pick<
+ EdgeType,
+ 'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'
+> &
EdgePosition &
EdgeLabelOptions & {
sourceHandleId?: string | null;
diff --git a/packages/react/src/types/general.ts b/packages/react/src/types/general.ts
index 0e1e1dc4..6f723237 100644
--- a/packages/react/src/types/general.ts
+++ b/packages/react/src/types/general.ts
@@ -1,3 +1,4 @@
+import { ComponentType } from 'react';
import {
FitViewParamsBase,
FitViewOptionsBase,
@@ -9,13 +10,19 @@ import {
SetCenter,
FitBounds,
XYPosition,
- NodeProps,
OnBeforeDeleteBase,
Connection,
} from '@xyflow/system';
-import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps } from '.';
-import { ComponentType } from 'react';
+import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps, NodeProps } from '.';
+
+// this is needed, to use generics + forwardRef
+declare module 'react' {
+ // eslint-disable-next-line @typescript-eslint/ban-types
+ function forwardRef(
+ render: (props: P, ref: React.Ref) => React.ReactElement | null
+ ): (props: P & React.RefAttributes) => React.ReactElement | null;
+}
export type OnNodesChange = (changes: NodeChange[]) => void;
export type OnEdgesChange = (changes: EdgeChange[]) => void;
@@ -27,8 +34,28 @@ export type OnDelete
edges: EdgeType[];
}) => void;
-export type NodeTypes = { [key: string]: ComponentType };
-export type EdgeTypes = { [key: string]: ComponentType };
+export type NodeTypes = Record<
+ string,
+ ComponentType<
+ NodeProps & {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ data: any;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ type: any;
+ }
+ >
+>;
+export type EdgeTypes = Record<
+ string,
+ ComponentType<
+ EdgeProps & {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ data: any;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ type: any;
+ }
+ >
+>;
export type UnselectNodesAndEdgesParams = {
nodes?: Node[];
diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts
index c3c0ea94..52f5a321 100644
--- a/packages/react/src/types/nodes.ts
+++ b/packages/react/src/types/nodes.ts
@@ -1,5 +1,5 @@
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
-import type { CoordinateExtent, NodeBase, NodeOrigin, OnError } from '@xyflow/system';
+import type { CoordinateExtent, NodeBase, NodeOrigin, OnError, NodeProps as NodePropsBase } from '@xyflow/system';
import { NodeTypes } from './general';
@@ -9,7 +9,7 @@ import { NodeTypes } from './general';
*/
export type Node<
NodeData extends Record = Record,
- NodeType extends string | undefined = string | undefined
+ NodeType extends string = string
> = NodeBase & {
style?: CSSProperties;
className?: string;
@@ -49,3 +49,5 @@ export type NodeWrapperProps = {
};
export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>;
+
+export type NodeProps = NodePropsBase;
diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md
index 4635387f..f784ff70 100644
--- a/packages/svelte/CHANGELOG.md
+++ b/packages/svelte/CHANGELOG.md
@@ -1,5 +1,16 @@
# @xyflow/svelte
+## 0.0.38
+
+## ⚠️ Breaking changes
+
+- `NodeProps` generic is a node and not only node data. `type $$Props = NodeProps`
+
+## Patch changes
+
+- unify `Edge` and `Node` type handling
+- fix safari: prevent selection of viewport
+
## 0.0.37
## ⚠️ Breaking changes
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index f73908cb..02b23545 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -1,6 +1,6 @@
{
"name": "@xyflow/svelte",
- "version": "0.0.37",
+ "version": "0.0.38",
"description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.",
"keywords": [
"svelte",
@@ -46,9 +46,9 @@
"classcat": "^5.0.4"
},
"devDependencies": {
- "@sveltejs/adapter-auto": "^2.1.0",
- "@sveltejs/kit": "^1.22.6",
- "@sveltejs/package": "^2.2.1",
+ "@sveltejs/adapter-auto": "^3.1.1",
+ "@sveltejs/kit": "^2.5.2",
+ "@sveltejs/package": "^2.2.7",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"@typescript-eslint/parser": "^5.60.0",
"autoprefixer": "^10.4.15",
@@ -65,8 +65,8 @@
"postcss-rename": "^0.6.1",
"prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.10.1",
- "svelte": "^4.2.0",
- "svelte-check": "^3.5.0",
+ "svelte": "^4.2.12",
+ "svelte-check": "^3.6.6",
"svelte-eslint-parser": "^0.32.2",
"tslib": "^2.5.3",
"typescript": "5.1.3"
diff --git a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte
index 1dff49e7..0a229099 100644
--- a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte
+++ b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte
@@ -51,7 +51,8 @@
edgecontextmenu: { edge: Edge; event: MouseEvent };
}>();
- $: edgeComponent = $edgeTypes[type!] || BezierEdgeInternal;
+ const edgeType = type || 'default';
+ $: edgeComponent = $edgeTypes[edgeType] || BezierEdgeInternal;
$: markerStartUrl = markerStart ? `url(#${getMarkerId(markerStart, $flowId)})` : undefined;
$: markerEndUrl = markerEnd ? `url(#${getMarkerId(markerEnd, $flowId)})` : undefined;
$: isSelectable = selectable || ($elementsSelectable && typeof selectable === 'undefined');
@@ -113,6 +114,7 @@
{data}
{style}
{interactionWidth}
+ type={edgeType}
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 50dda4e7..04dcfc8c 100644
--- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte
+++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte
@@ -1,16 +1,10 @@