From d377a4d1a4b254e8b5e02ff3c2830c3c0a6bd5f0 Mon Sep 17 00:00:00 2001 From: moklick Date: Fri, 15 Nov 2024 12:18:12 +0100 Subject: [PATCH 01/23] chore(react): pass edge type to IsValidConnection prop closes #4813 --- packages/react/src/types/component-props.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/types/component-props.ts b/packages/react/src/types/component-props.ts index 623992b2..d04875d1 100644 --- a/packages/react/src/types/component-props.ts +++ b/packages/react/src/types/component-props.ts @@ -499,7 +499,7 @@ export interface ReactFlowProps true */ - isValidConnection?: IsValidConnection; + isValidConnection?: IsValidConnection; /** With a threshold greater than zero you can control the distinction between node drag and click events. * * If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired. From 43aa52a8cd93e6a01c01afa314c7036eb5b77131 Mon Sep 17 00:00:00 2001 From: moklick Date: Fri, 15 Nov 2024 12:21:32 +0100 Subject: [PATCH 02/23] chore(changeset): add --- .changeset/quick-carrots-allow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/quick-carrots-allow.md diff --git a/.changeset/quick-carrots-allow.md b/.changeset/quick-carrots-allow.md new file mode 100644 index 00000000..960c3815 --- /dev/null +++ b/.changeset/quick-carrots-allow.md @@ -0,0 +1,5 @@ +--- +'@xyflow/react': patch +--- + +Type isValidConnection prop correctly by passing EdgeType From 1e38733da5fc6591a787a01fa0a9853e9ee4339f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Mon, 18 Nov 2024 14:11:52 +0100 Subject: [PATCH 03/23] chore(readme): update team --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f6ce20b..585dac0e 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ React Flow and Svelte Flow are maintained by the team behind [xyflow](https://xy - Christopher • [Twitter](https://twitter.com/chrtze) • [Github](https://github.com/chrtze) - Hayleigh • [Twitter](https://twitter.com/hayleighdotdev) • [Github](https://github.com/hayleigh-dot-dev) -- John • [Website](https://johnrobbdesign.com/) • [Mastodon](https://mastodon.social/@johnrobbjr) +- Abbey • [Github](https://github.com/printerscanner) - Moritz • [Twitter](https://twitter.com/moklick) • [Github](https://github.com/moklick) - Peter • [Github](https://github.com/peterkogo) From 02532fcdc5e8d600105c5cdb25883962223fb2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Mon, 18 Nov 2024 14:56:58 +0100 Subject: [PATCH 04/23] fix(panel): forward ref to inner panel div --- packages/react/src/components/Panel/index.tsx | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/react/src/components/Panel/index.tsx b/packages/react/src/components/Panel/index.tsx index eeb01a15..83b4a87b 100644 --- a/packages/react/src/components/Panel/index.tsx +++ b/packages/react/src/components/Panel/index.tsx @@ -1,4 +1,4 @@ -import type { HTMLAttributes, ReactNode } from 'react'; +import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'; import cc from 'classcat'; import type { PanelPosition } from '@xyflow/system'; @@ -15,17 +15,20 @@ export type PanelProps = HTMLAttributes & { const selector = (s: ReactFlowState) => (s.userSelectionActive ? 'none' : 'all'); -export function Panel({ position = 'top-left', children, className, style, ...rest }: PanelProps) { - const pointerEvents = useStore(selector); - const positionClasses = `${position}`.split('-'); +export const Panel = forwardRef( + ({ position = 'top-left', children, className, style, ...rest }, ref) => { + const pointerEvents = useStore(selector); + const positionClasses = `${position}`.split('-'); - return ( -
- {children} -
- ); -} + return ( +
+ {children} +
+ ); + } +); From 5f90acdab1f4748bb04f8b1ec67d569983684058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Mon, 18 Nov 2024 15:40:10 +0100 Subject: [PATCH 05/23] chore(changeset): add --- .changeset/thick-fans-cheer.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/thick-fans-cheer.md diff --git a/.changeset/thick-fans-cheer.md b/.changeset/thick-fans-cheer.md new file mode 100644 index 00000000..da57aec5 --- /dev/null +++ b/.changeset/thick-fans-cheer.md @@ -0,0 +1,5 @@ +--- +'@xyflow/react': patch +--- + +forward ref to the inner div of the panel component From fbeab9c11d0791319bbb3bee1dee407e462d7b7a Mon Sep 17 00:00:00 2001 From: moklick Date: Sun, 24 Nov 2024 14:15:01 +0100 Subject: [PATCH 06/23] chore(react): allow custom data-testid #4838 --- packages/react/src/container/ReactFlow/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/container/ReactFlow/index.tsx b/packages/react/src/container/ReactFlow/index.tsx index 962b6846..82206fc8 100644 --- a/packages/react/src/container/ReactFlow/index.tsx +++ b/packages/react/src/container/ReactFlow/index.tsx @@ -153,11 +153,11 @@ function ReactFlow( return (
Date: Sun, 24 Nov 2024 14:18:01 +0100 Subject: [PATCH 07/23] chore(changeset): add --- .changeset/old-lobsters-cover.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/old-lobsters-cover.md diff --git a/.changeset/old-lobsters-cover.md b/.changeset/old-lobsters-cover.md new file mode 100644 index 00000000..82795889 --- /dev/null +++ b/.changeset/old-lobsters-cover.md @@ -0,0 +1,5 @@ +--- +'@xyflow/react': patch +--- + +Allow custom data-testid for ReactFlow component From 7043700192e48773ad3ccd5d23e7839134025841 Mon Sep 17 00:00:00 2001 From: moklick Date: Sun, 24 Nov 2024 15:01:28 +0100 Subject: [PATCH 08/23] fix(expandParent: do not mutate change object #4829 --- packages/react/src/store/index.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index 067f4093..2466baa5 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -148,26 +148,30 @@ const createStore = ({ const changes = []; for (const [id, dragItem] of nodeDragItems) { + const expandParent = !!(dragItem?.expandParent && dragItem?.parentId && dragItem?.position); + const change: NodeChange = { id, type: 'position', - position: dragItem.position, + position: expandParent + ? { + x: Math.max(0, dragItem.position.x), + y: Math.max(0, dragItem.position.y), + } + : dragItem.position, dragging, }; - if (dragItem?.expandParent && dragItem?.parentId && change.position) { + if (expandParent) { parentExpandChildren.push({ id, - parentId: dragItem.parentId, + parentId: dragItem.parentId!, rect: { ...dragItem.internals.positionAbsolute, width: dragItem.measured.width!, height: dragItem.measured.height!, }, }); - - change.position.x = Math.max(0, change.position.x); - change.position.y = Math.max(0, change.position.y); } changes.push(change); From 7501793900fdb125fe906fa3d9d83f1bd11c6e20 Mon Sep 17 00:00:00 2001 From: moklick Date: Sun, 24 Nov 2024 15:02:41 +0100 Subject: [PATCH 09/23] chore(changeset): add --- .changeset/blue-teachers-sell.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/blue-teachers-sell.md diff --git a/.changeset/blue-teachers-sell.md b/.changeset/blue-teachers-sell.md new file mode 100644 index 00000000..f0d944aa --- /dev/null +++ b/.changeset/blue-teachers-sell.md @@ -0,0 +1,5 @@ +--- +'@xyflow/react': patch +--- + +Make it possible to use expandParent with immer and other immutable helpers From 190e5ccc1c0733534d1498ae97a6d0100217db85 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Thu, 28 Nov 2024 15:55:15 +0100 Subject: [PATCH 10/23] adjusted wording of changeset --- .changeset/thick-fans-cheer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/thick-fans-cheer.md b/.changeset/thick-fans-cheer.md index da57aec5..af987d35 100644 --- a/.changeset/thick-fans-cheer.md +++ b/.changeset/thick-fans-cheer.md @@ -2,4 +2,4 @@ '@xyflow/react': patch --- -forward ref to the inner div of the panel component +Forward ref of the div inside Panel components. From 23b3fb64c5c718bc30e2e287df448542c0a7a21d Mon Sep 17 00:00:00 2001 From: Matt Huggins Date: Thu, 28 Nov 2024 17:17:58 -0600 Subject: [PATCH 11/23] feat: accept any `path` element attribute as a prop --- packages/react/src/components/Edges/BaseEdge.tsx | 2 ++ packages/react/src/components/Edges/BezierEdge.tsx | 2 ++ .../react/src/components/Edges/SimpleBezierEdge.tsx | 2 ++ .../react/src/components/Edges/SmoothStepEdge.tsx | 2 ++ packages/react/src/components/Edges/StraightEdge.tsx | 3 +++ packages/react/src/types/edges.ts | 12 +++++------- 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/react/src/components/Edges/BaseEdge.tsx b/packages/react/src/components/Edges/BaseEdge.tsx index fab06220..d3d6999d 100644 --- a/packages/react/src/components/Edges/BaseEdge.tsx +++ b/packages/react/src/components/Edges/BaseEdge.tsx @@ -20,10 +20,12 @@ export function BaseEdge({ markerStart, className, interactionWidth = 20, + ...props }: BaseEdgeProps) { return ( <> { const [path, labelX, labelY] = getBezierPath({ sourceX, @@ -41,6 +42,7 @@ function createBezierEdge(params: { isInternal: boolean }) { return ( { const [path, labelX, labelY] = getSimpleBezierPath({ sourceX, @@ -106,6 +107,7 @@ function createSimpleBezierEdge(params: { isInternal: boolean }) { return ( { const [path, labelX, labelY] = getSmoothStepPath({ sourceX, @@ -42,6 +43,7 @@ function createSmoothStepEdge(params: { isInternal: boolean }) { return ( { const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY }); @@ -30,6 +31,7 @@ function createStraightEdge(params: { isInternal: boolean }) { return ( ); } diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index 8b0cebab..8ca672dc 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -92,6 +92,8 @@ export type EdgeWrapperProps = { disableKeyboardA11y?: boolean; }; +type BasePathAttributes = Omit, "d">; + export type DefaultEdgeOptions = DefaultEdgeOptionsBase; export type EdgeTextProps = HTMLAttributes & @@ -123,12 +125,9 @@ export type EdgeProps = Pick< * BaseEdge component props * @public */ -export type BaseEdgeProps = EdgeLabelOptions & { - /** Unique id of edge */ - id?: string; +export type BaseEdgeProps = BasePathAttributes & EdgeLabelOptions & { /** Additional padding where interacting with an edge is still possible */ interactionWidth?: number; - className?: string; /** The x position of edge label */ labelX?: number; /** The y position of edge label */ @@ -143,14 +142,13 @@ export type BaseEdgeProps = EdgeLabelOptions & { markerEnd?: string; /** SVG path of the edge */ path: string; - style?: CSSProperties; }; /** * Helper type for edge components that get exported by the library * @public */ -export type EdgeComponentProps = EdgePosition & +export type EdgeComponentProps = BasePathAttributes & EdgePosition & EdgeLabelOptions & { id?: EdgeProps['id']; markerStart?: EdgeProps['markerStart']; @@ -187,7 +185,7 @@ export type StepEdgeProps = EdgeComponentWithPathOptions; * StraightEdge component props * @public */ -export type StraightEdgeProps = Omit; +export type StraightEdgeProps = HTMLAttributes & Omit; /** * SimpleBezier component props From 106c2cf8e5e9a944ea7ea9a2558e26cb75f9ac93 Mon Sep 17 00:00:00 2001 From: Matt Huggins Date: Thu, 28 Nov 2024 17:31:56 -0600 Subject: [PATCH 12/23] fixup! feat: accept any `path` element attribute as a prop --- .changeset/selfish-tables-tie.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/selfish-tables-tie.md diff --git a/.changeset/selfish-tables-tie.md b/.changeset/selfish-tables-tie.md new file mode 100644 index 00000000..6d4eb598 --- /dev/null +++ b/.changeset/selfish-tables-tie.md @@ -0,0 +1,5 @@ +--- +'@xyflow/react': minor +--- + +Support all `path` element attributes in React edge components. From 9458904cbb1f19bcb8e61f66a0122ace4b0033e1 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:29:43 +0100 Subject: [PATCH 13/23] fix(react): prevent default scrolling behavior when node is moved with keypress --- packages/react/src/components/NodeWrapper/index.tsx | 3 +++ packages/react/src/components/NodesSelection/index.tsx | 2 ++ 2 files changed, 5 insertions(+) diff --git a/packages/react/src/components/NodeWrapper/index.tsx b/packages/react/src/components/NodeWrapper/index.tsx index 5f528486..a54a4993 100644 --- a/packages/react/src/components/NodeWrapper/index.tsx +++ b/packages/react/src/components/NodeWrapper/index.tsx @@ -138,6 +138,9 @@ export function NodeWrapper({ nodeRef, }); } else if (isDraggable && node.selected && Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) { + // prevent default scrolling behavior on arrow key press when node is moved + event.preventDefault(); + store.setState({ ariaLiveMessage: `Moved selected node ${event.key .replace('Arrow', '') diff --git a/packages/react/src/components/NodesSelection/index.tsx b/packages/react/src/components/NodesSelection/index.tsx index e2bcd238..ecaef17b 100644 --- a/packages/react/src/components/NodesSelection/index.tsx +++ b/packages/react/src/components/NodesSelection/index.tsx @@ -68,6 +68,8 @@ export function NodesSelection({ const onKeyDown = (event: KeyboardEvent) => { if (Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) { + event.preventDefault(); + moveSelectedNodes({ direction: arrowKeyDiffs[event.key], factor: event.shiftKey ? 4 : 1, From adf4fb4e7bb136cce47ef540e9bcc0f82cf17ff4 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:30:14 +0100 Subject: [PATCH 14/23] chore(changeset): add --- .changeset/silly-beans-remain.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silly-beans-remain.md diff --git a/.changeset/silly-beans-remain.md b/.changeset/silly-beans-remain.md new file mode 100644 index 00000000..42c8b49e --- /dev/null +++ b/.changeset/silly-beans-remain.md @@ -0,0 +1,5 @@ +--- +'@xyflow/react': patch +--- + +Prevent default scrolling behavior when nodes or a selection is moved with an arrow key press. From ca66999634301bebe954a8fdd274f3654f7f59d8 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 2 Dec 2024 13:37:17 +0100 Subject: [PATCH 15/23] simplified prop drilling & improved changeset --- .changeset/selfish-tables-tie.md | 2 +- .../react/src/components/Edges/BaseEdge.tsx | 23 +++++++-- .../react/src/components/Edges/BezierEdge.tsx | 30 +----------- .../src/components/Edges/SimpleBezierEdge.tsx | 30 +----------- .../src/components/Edges/SmoothStepEdge.tsx | 30 +----------- .../src/components/Edges/StraightEdge.tsx | 48 ++----------------- packages/react/src/types/edges.ts | 48 ++++++++++--------- 7 files changed, 53 insertions(+), 158 deletions(-) diff --git a/.changeset/selfish-tables-tie.md b/.changeset/selfish-tables-tie.md index 6d4eb598..94d9db0b 100644 --- a/.changeset/selfish-tables-tie.md +++ b/.changeset/selfish-tables-tie.md @@ -2,4 +2,4 @@ '@xyflow/react': minor --- -Support all `path` element attributes in React edge components. +Support passing `path` element attributes to BaseEdge component. diff --git a/packages/react/src/components/Edges/BaseEdge.tsx b/packages/react/src/components/Edges/BaseEdge.tsx index d3d6999d..ca7116a5 100644 --- a/packages/react/src/components/Edges/BaseEdge.tsx +++ b/packages/react/src/components/Edges/BaseEdge.tsx @@ -2,7 +2,7 @@ import { isNumeric } from '@xyflow/system'; import cc from 'classcat'; import { EdgeText } from './EdgeText'; -import type { BaseEdgeProps } from '../../types'; +import type { BaseEdgeProps, EdgeComponentWithPathOptions, EdgeProps } from '../../types'; export function BaseEdge({ id, @@ -15,19 +15,34 @@ export function BaseEdge({ labelBgStyle, labelBgPadding, labelBgBorderRadius, - style, markerEnd, markerStart, className, interactionWidth = 20, ...props }: BaseEdgeProps) { + // We are not allowed to pass these props to the path element otherwise we get a bunch of warnings + const { + animated, + selectable, + deletable, + data, + selected, + source, + target, + targetHandleId, + sourceHandleId, + targetPosition, + sourcePosition, + pathOptions, + ...restProps + } = props as EdgeComponentWithPathOptions & EdgeProps; + return ( <> { const [path, labelX, labelY] = getBezierPath({ @@ -40,25 +30,7 @@ function createBezierEdge(params: { isInternal: boolean }) { const _id = params.isInternal ? undefined : id; - return ( - - ); + return ; } ); } diff --git a/packages/react/src/components/Edges/SimpleBezierEdge.tsx b/packages/react/src/components/Edges/SimpleBezierEdge.tsx index 85a2aa3a..6aff362a 100644 --- a/packages/react/src/components/Edges/SimpleBezierEdge.tsx +++ b/packages/react/src/components/Edges/SimpleBezierEdge.tsx @@ -82,16 +82,6 @@ function createSimpleBezierEdge(params: { isInternal: boolean }) { targetY, sourcePosition = Position.Bottom, targetPosition = Position.Top, - label, - labelStyle, - labelShowBg, - labelBgStyle, - labelBgPadding, - labelBgBorderRadius, - style, - markerEnd, - markerStart, - interactionWidth, ...props }: SimpleBezierEdgeProps) => { const [path, labelX, labelY] = getSimpleBezierPath({ @@ -105,25 +95,7 @@ function createSimpleBezierEdge(params: { isInternal: boolean }) { const _id = params.isInternal ? undefined : id; - return ( - - ); + return ; } ); } diff --git a/packages/react/src/components/Edges/SmoothStepEdge.tsx b/packages/react/src/components/Edges/SmoothStepEdge.tsx index 01e97499..8d362427 100644 --- a/packages/react/src/components/Edges/SmoothStepEdge.tsx +++ b/packages/react/src/components/Edges/SmoothStepEdge.tsx @@ -13,19 +13,9 @@ function createSmoothStepEdge(params: { isInternal: boolean }) { sourceY, targetX, targetY, - label, - labelStyle, - labelShowBg, - labelBgStyle, - labelBgPadding, - labelBgBorderRadius, - style, sourcePosition = Position.Bottom, targetPosition = Position.Top, - markerEnd, - markerStart, pathOptions, - interactionWidth, ...props }: SmoothStepEdgeProps) => { const [path, labelX, labelY] = getSmoothStepPath({ @@ -41,25 +31,7 @@ function createSmoothStepEdge(params: { isInternal: boolean }) { const _id = params.isInternal ? undefined : id; - return ( - - ); + return ; } ); } diff --git a/packages/react/src/components/Edges/StraightEdge.tsx b/packages/react/src/components/Edges/StraightEdge.tsx index 102aa484..5b767638 100644 --- a/packages/react/src/components/Edges/StraightEdge.tsx +++ b/packages/react/src/components/Edges/StraightEdge.tsx @@ -6,51 +6,13 @@ import type { StraightEdgeProps } from '../../types'; function createStraightEdge(params: { isInternal: boolean }) { // eslint-disable-next-line react/display-name - return memo( - ({ - id, - sourceX, - sourceY, - targetX, - targetY, - label, - labelStyle, - labelShowBg, - labelBgStyle, - labelBgPadding, - labelBgBorderRadius, - style, - markerEnd, - markerStart, - interactionWidth, - ...props - }: StraightEdgeProps) => { - const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY }); + return memo(({ id, sourceX, sourceY, targetX, targetY, ...props }: StraightEdgeProps) => { + const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY }); - const _id = params.isInternal ? undefined : id; + const _id = params.isInternal ? undefined : id; - return ( - - ); - } - ); + return ; + }); } const StraightEdge = createStraightEdge({ isInternal: false }); diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index 8ca672dc..7de41f42 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -14,7 +14,6 @@ import type { EdgePosition, StepPathOptions, OnError, - ConnectionState, FinalConnectionState, } from '@xyflow/system'; @@ -92,8 +91,6 @@ export type EdgeWrapperProps = { disableKeyboardA11y?: boolean; }; -type BasePathAttributes = Omit, "d">; - export type DefaultEdgeOptions = DefaultEdgeOptionsBase; export type EdgeTextProps = HTMLAttributes & @@ -121,34 +118,38 @@ export type EdgeProps = Pick< interactionWidth?: number; }; +type BasePathAttributes = Omit, 'd'>; + /** * BaseEdge component props * @public */ -export type BaseEdgeProps = BasePathAttributes & EdgeLabelOptions & { - /** Additional padding where interacting with an edge is still possible */ - interactionWidth?: number; - /** The x position of edge label */ - labelX?: number; - /** The y position of edge label */ - labelY?: number; - /** Marker at start of edge - * @example 'url(#arrow)' - */ - markerStart?: string; - /** Marker at end of edge - * @example 'url(#arrow)' - */ - markerEnd?: string; - /** SVG path of the edge */ - path: string; -}; +export type BaseEdgeProps = BasePathAttributes & + EdgeLabelOptions & { + /** Additional padding where interacting with an edge is still possible */ + interactionWidth?: number; + /** The x position of edge label */ + labelX?: number; + /** The y position of edge label */ + labelY?: number; + /** Marker at start of edge + * @example 'url(#arrow)' + */ + markerStart?: string; + /** Marker at end of edge + * @example 'url(#arrow)' + */ + markerEnd?: string; + /** SVG path of the edge */ + path: string; + }; /** * Helper type for edge components that get exported by the library * @public */ -export type EdgeComponentProps = BasePathAttributes & EdgePosition & +export type EdgeComponentProps = BasePathAttributes & + EdgePosition & EdgeLabelOptions & { id?: EdgeProps['id']; markerStart?: EdgeProps['markerStart']; @@ -185,7 +186,8 @@ export type StepEdgeProps = EdgeComponentWithPathOptions; * StraightEdge component props * @public */ -export type StraightEdgeProps = HTMLAttributes & Omit; +export type StraightEdgeProps = HTMLAttributes & + Omit; /** * SimpleBezier component props From 8b8ef2bffa03e12d59ce77d334f02576f9e49929 Mon Sep 17 00:00:00 2001 From: Matt Huggins Date: Mon, 2 Dec 2024 10:20:52 -0600 Subject: [PATCH 16/23] HTMLAttributes -> SVGAttributes, add edge example --- .../react/src/examples/Edges/CustomEdge3.css | 14 +++++++ .../react/src/examples/Edges/CustomEdge3.tsx | 38 +++++++++++++++++++ examples/react/src/examples/Edges/index.tsx | 15 ++++++++ packages/react/src/types/edges.ts | 15 ++++++-- 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 examples/react/src/examples/Edges/CustomEdge3.css create mode 100644 examples/react/src/examples/Edges/CustomEdge3.tsx diff --git a/examples/react/src/examples/Edges/CustomEdge3.css b/examples/react/src/examples/Edges/CustomEdge3.css new file mode 100644 index 00000000..570b7c75 --- /dev/null +++ b/examples/react/src/examples/Edges/CustomEdge3.css @@ -0,0 +1,14 @@ +@keyframes react-flow-edge-dash { + from { + stroke-dashoffset: 100; + } + to { + stroke-dashoffset: 0; + } +} + +.react-flow__edge-custom3 { + stroke-dasharray: 100; + stroke-dashoffset: 100; + animation: react-flow-edge-dash 1s linear forwards; +} diff --git a/examples/react/src/examples/Edges/CustomEdge3.tsx b/examples/react/src/examples/Edges/CustomEdge3.tsx new file mode 100644 index 00000000..37a3d40f --- /dev/null +++ b/examples/react/src/examples/Edges/CustomEdge3.tsx @@ -0,0 +1,38 @@ +import { FC } from 'react'; +import { BaseEdge, EdgeProps, EdgeText, getSmoothStepPath } from '@xyflow/react'; +import './CustomEdge3.css'; + +const CustomEdge: FC = ({ + id, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + data, +}) => { + const [edgePath, labelX, labelY] = getSmoothStepPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + + return ( + <> + + console.log(data)} + /> + + ); +}; + +export default CustomEdge; diff --git a/examples/react/src/examples/Edges/index.tsx b/examples/react/src/examples/Edges/index.tsx index bbba30fb..6e18d24a 100644 --- a/examples/react/src/examples/Edges/index.tsx +++ b/examples/react/src/examples/Edges/index.tsx @@ -16,6 +16,7 @@ import { import CustomEdge from './CustomEdge'; import CustomEdge2 from './CustomEdge2'; +import CustomEdge3 from './CustomEdge3'; const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); @@ -63,6 +64,12 @@ const initialNodes: Node[] = [ data: { label: 'Output 9' }, position: { x: 675, y: 500 }, }, + { + id: '10', + type: 'output', + data: { label: 'Output 10' }, + position: { x: 50, y: 400 }, + }, ]; const initialEdges: Edge[] = [ @@ -137,6 +144,13 @@ const initialEdges: Edge[] = [ type: 'custom2', data: { text: 'custom edge 2' }, }, + { + id: 'e3a-10', + source: '3a', + target: '10', + type: 'custom3', + data: { text: 'custom edge 3' }, + }, { id: 'e5-6', source: '5', @@ -173,6 +187,7 @@ const initialEdges: Edge[] = [ const edgeTypes: EdgeTypes = { custom: CustomEdge, custom2: CustomEdge2, + custom3: CustomEdge3, }; const defaultEdgeOptions = { diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index 7de41f42..1a0a3353 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -1,6 +1,13 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { CSSProperties, HTMLAttributes, ReactNode, MouseEvent as ReactMouseEvent, ComponentType } from 'react'; +import type { + CSSProperties, + HTMLAttributes, + SVGAttributes, + ReactNode, + MouseEvent as ReactMouseEvent, + ComponentType, +} from 'react'; import type { EdgeBase, BezierPathOptions, @@ -93,7 +100,7 @@ export type EdgeWrapperProps = { export type DefaultEdgeOptions = DefaultEdgeOptionsBase; -export type EdgeTextProps = HTMLAttributes & +export type EdgeTextProps = SVGAttributes & EdgeLabelOptions & { x: number; y: number; @@ -118,7 +125,7 @@ export type EdgeProps = Pick< interactionWidth?: number; }; -type BasePathAttributes = Omit, 'd'>; +type BasePathAttributes = Omit, 'd'>; /** * BaseEdge component props @@ -186,7 +193,7 @@ export type StepEdgeProps = EdgeComponentWithPathOptions; * StraightEdge component props * @public */ -export type StraightEdgeProps = HTMLAttributes & +export type StraightEdgeProps = SVGAttributes & Omit; /** From 3526963f0ba8d708f0250e8c3f161edbdae2347f Mon Sep 17 00:00:00 2001 From: peterkogo Date: Wed, 4 Dec 2024 11:12:49 +0100 Subject: [PATCH 17/23] Revert back to passing props manually --- .../react/src/components/Edges/BaseEdge.tsx | 19 +------- .../react/src/components/Edges/BezierEdge.tsx | 30 ++++++++++++- .../src/components/Edges/SimpleBezierEdge.tsx | 30 ++++++++++++- .../src/components/Edges/SmoothStepEdge.tsx | 30 ++++++++++++- .../src/components/Edges/StraightEdge.tsx | 45 ++++++++++++++++--- packages/react/src/types/edges.ts | 7 +-- 6 files changed, 127 insertions(+), 34 deletions(-) diff --git a/packages/react/src/components/Edges/BaseEdge.tsx b/packages/react/src/components/Edges/BaseEdge.tsx index ca7116a5..c03c5af9 100644 --- a/packages/react/src/components/Edges/BaseEdge.tsx +++ b/packages/react/src/components/Edges/BaseEdge.tsx @@ -21,27 +21,10 @@ export function BaseEdge({ interactionWidth = 20, ...props }: BaseEdgeProps) { - // We are not allowed to pass these props to the path element otherwise we get a bunch of warnings - const { - animated, - selectable, - deletable, - data, - selected, - source, - target, - targetHandleId, - sourceHandleId, - targetPosition, - sourcePosition, - pathOptions, - ...restProps - } = props as EdgeComponentWithPathOptions & EdgeProps; - return ( <> { const [path, labelX, labelY] = getBezierPath({ sourceX, @@ -30,7 +39,24 @@ function createBezierEdge(params: { isInternal: boolean }) { const _id = params.isInternal ? undefined : id; - return ; + return ( + + ); } ); } diff --git a/packages/react/src/components/Edges/SimpleBezierEdge.tsx b/packages/react/src/components/Edges/SimpleBezierEdge.tsx index 6aff362a..b3378c48 100644 --- a/packages/react/src/components/Edges/SimpleBezierEdge.tsx +++ b/packages/react/src/components/Edges/SimpleBezierEdge.tsx @@ -82,7 +82,16 @@ function createSimpleBezierEdge(params: { isInternal: boolean }) { targetY, sourcePosition = Position.Bottom, targetPosition = Position.Top, - ...props + label, + labelStyle, + labelShowBg, + labelBgStyle, + labelBgPadding, + labelBgBorderRadius, + style, + markerEnd, + markerStart, + interactionWidth, }: SimpleBezierEdgeProps) => { const [path, labelX, labelY] = getSimpleBezierPath({ sourceX, @@ -95,7 +104,24 @@ function createSimpleBezierEdge(params: { isInternal: boolean }) { const _id = params.isInternal ? undefined : id; - return ; + return ( + + ); } ); } diff --git a/packages/react/src/components/Edges/SmoothStepEdge.tsx b/packages/react/src/components/Edges/SmoothStepEdge.tsx index 8d362427..1f6bcfd1 100644 --- a/packages/react/src/components/Edges/SmoothStepEdge.tsx +++ b/packages/react/src/components/Edges/SmoothStepEdge.tsx @@ -13,10 +13,19 @@ function createSmoothStepEdge(params: { isInternal: boolean }) { sourceY, targetX, targetY, + label, + labelStyle, + labelShowBg, + labelBgStyle, + labelBgPadding, + labelBgBorderRadius, + style, sourcePosition = Position.Bottom, targetPosition = Position.Top, + markerEnd, + markerStart, pathOptions, - ...props + interactionWidth, }: SmoothStepEdgeProps) => { const [path, labelX, labelY] = getSmoothStepPath({ sourceX, @@ -31,7 +40,24 @@ function createSmoothStepEdge(params: { isInternal: boolean }) { const _id = params.isInternal ? undefined : id; - return ; + return ( + + ); } ); } diff --git a/packages/react/src/components/Edges/StraightEdge.tsx b/packages/react/src/components/Edges/StraightEdge.tsx index 5b767638..53d1ac29 100644 --- a/packages/react/src/components/Edges/StraightEdge.tsx +++ b/packages/react/src/components/Edges/StraightEdge.tsx @@ -6,13 +6,48 @@ import type { StraightEdgeProps } from '../../types'; function createStraightEdge(params: { isInternal: boolean }) { // eslint-disable-next-line react/display-name - return memo(({ id, sourceX, sourceY, targetX, targetY, ...props }: StraightEdgeProps) => { - const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY }); + return memo( + ({ + id, + sourceX, + sourceY, + targetX, + targetY, + label, + labelStyle, + labelShowBg, + labelBgStyle, + labelBgPadding, + labelBgBorderRadius, + style, + markerEnd, + markerStart, + interactionWidth, + }: StraightEdgeProps) => { + const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY }); - const _id = params.isInternal ? undefined : id; + const _id = params.isInternal ? undefined : id; - return ; - }); + return ( + + ); + } + ); } const StraightEdge = createStraightEdge({ isInternal: false }); diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index 1a0a3353..caaea501 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -125,13 +125,11 @@ export type EdgeProps = Pick< interactionWidth?: number; }; -type BasePathAttributes = Omit, 'd'>; - /** * BaseEdge component props * @public */ -export type BaseEdgeProps = BasePathAttributes & +export type BaseEdgeProps = Omit, 'd'> & EdgeLabelOptions & { /** Additional padding where interacting with an edge is still possible */ interactionWidth?: number; @@ -155,8 +153,7 @@ export type BaseEdgeProps = BasePathAttributes & * Helper type for edge components that get exported by the library * @public */ -export type EdgeComponentProps = BasePathAttributes & - EdgePosition & +export type EdgeComponentProps = EdgePosition & EdgeLabelOptions & { id?: EdgeProps['id']; markerStart?: EdgeProps['markerStart']; From cf3bae57b14c9bea1743c98dc8b641e3468e0b55 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Wed, 4 Dec 2024 11:17:00 +0100 Subject: [PATCH 18/23] removed svg props from straight edge --- packages/react/src/types/edges.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index caaea501..c2463e25 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -190,8 +190,7 @@ export type StepEdgeProps = EdgeComponentWithPathOptions; * StraightEdge component props * @public */ -export type StraightEdgeProps = SVGAttributes & - Omit; +export type StraightEdgeProps = Omit; /** * SimpleBezier component props From cc6b1d97179935bbcddd4bd97a2db752a9af3993 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Wed, 4 Dec 2024 11:25:22 +0100 Subject: [PATCH 19/23] remove unsused import --- packages/react/src/types/edges.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index c2463e25..83729ddd 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -1,13 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { - CSSProperties, - HTMLAttributes, - SVGAttributes, - ReactNode, - MouseEvent as ReactMouseEvent, - ComponentType, -} from 'react'; +import type { CSSProperties, SVGAttributes, ReactNode, MouseEvent as ReactMouseEvent, ComponentType } from 'react'; import type { EdgeBase, BezierPathOptions, From 7a8e53463e3e854692a4cd3c4c698831833bb006 Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 4 Dec 2024 12:17:35 +0100 Subject: [PATCH 20/23] chore(react): cleanup base edge --- packages/react/src/components/Edges/BaseEdge.tsx | 16 ++-------------- packages/react/src/types/edges.ts | 8 -------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/packages/react/src/components/Edges/BaseEdge.tsx b/packages/react/src/components/Edges/BaseEdge.tsx index c03c5af9..3d951796 100644 --- a/packages/react/src/components/Edges/BaseEdge.tsx +++ b/packages/react/src/components/Edges/BaseEdge.tsx @@ -2,10 +2,9 @@ import { isNumeric } from '@xyflow/system'; import cc from 'classcat'; import { EdgeText } from './EdgeText'; -import type { BaseEdgeProps, EdgeComponentWithPathOptions, EdgeProps } from '../../types'; +import type { BaseEdgeProps } from '../../types'; export function BaseEdge({ - id, path, labelX, labelY, @@ -15,23 +14,12 @@ export function BaseEdge({ labelBgStyle, labelBgPadding, labelBgBorderRadius, - markerEnd, - markerStart, - className, interactionWidth = 20, ...props }: BaseEdgeProps) { return ( <> - + {interactionWidth && ( , 'd'> & labelX?: number; /** The y position of edge label */ labelY?: number; - /** Marker at start of edge - * @example 'url(#arrow)' - */ - markerStart?: string; - /** Marker at end of edge - * @example 'url(#arrow)' - */ - markerEnd?: string; /** SVG path of the edge */ path: string; }; From 944238b015dd91efcc24a3b3277ba1095506b767 Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 4 Dec 2024 12:44:34 +0100 Subject: [PATCH 21/23] chore(changeset): update --- .changeset/selfish-tables-tie.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/selfish-tables-tie.md b/.changeset/selfish-tables-tie.md index 94d9db0b..8ac759cf 100644 --- a/.changeset/selfish-tables-tie.md +++ b/.changeset/selfish-tables-tie.md @@ -1,5 +1,5 @@ --- -'@xyflow/react': minor +'@xyflow/react': patch --- -Support passing `path` element attributes to BaseEdge component. +Support passing `path` element attributes to `BaseEdge` component. From a666888a1e8a7b44180b494ae7744ff3f404d1d5 Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 4 Dec 2024 13:07:05 +0100 Subject: [PATCH 22/23] refacotr(types): add group node to BuiltInNode type --- packages/react/src/types/nodes.ts | 4 +++- packages/svelte/src/lib/types/nodes.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts index f4ad2820..0d3924e4 100644 --- a/packages/react/src/types/nodes.ts +++ b/packages/react/src/types/nodes.ts @@ -56,6 +56,8 @@ export type NodeWrapperProps = { nodeClickDistance?: number; }; -export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>; +export type BuiltInNode = + | Node<{ label: string }, 'input' | 'output' | 'default'> + | Node, 'group'>; export type NodeProps = NodePropsBase; diff --git a/packages/svelte/src/lib/types/nodes.ts b/packages/svelte/src/lib/types/nodes.ts index 0e741c00..e5bd8552 100644 --- a/packages/svelte/src/lib/types/nodes.ts +++ b/packages/svelte/src/lib/types/nodes.ts @@ -44,7 +44,9 @@ export type NodeTypes = Record< export type DefaultNodeOptions = Partial>; -export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>; +export type BuiltInNode = + | Node<{ label: string }, 'input' | 'output' | 'default'> + | Node, 'group'>; export type NodeEventMap = { nodeclick: { node: Node; event: MouseEvent | TouchEvent }; From 2c4acc2bd9ec271468bd4c904e19d7fca627d9e1 Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 4 Dec 2024 13:07:59 +0100 Subject: [PATCH 23/23] chore(changeset): add --- .changeset/cuddly-mails-kneel.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/cuddly-mails-kneel.md diff --git a/.changeset/cuddly-mails-kneel.md b/.changeset/cuddly-mails-kneel.md new file mode 100644 index 00000000..2235a163 --- /dev/null +++ b/.changeset/cuddly-mails-kneel.md @@ -0,0 +1,6 @@ +--- +'@xyflow/react': patch +'@xyflow/svelte': patch +--- + +Add group node to BuiltInNode type