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 21306745..03934e40 100644 --- a/examples/react/src/examples/CustomNode/index.tsx +++ b/examples/react/src/examples/CustomNode/index.tsx @@ -17,6 +17,8 @@ import { OnBeforeDelete, BuiltInNode, BuiltInEdge, + NodeTypes, + ReactFlowProvider, } from '@xyflow/react'; import ColorSelectorNode from './ColorSelectorNode'; @@ -40,7 +42,7 @@ const initBgColor = '#1A192B'; const connectionLineStyle = { stroke: '#fff' }; const snapGrid: SnapGrid = [16, 16]; -const nodeTypes = { +const nodeTypes: NodeTypes = { selectorNode: ColorSelectorNode, }; @@ -188,4 +190,8 @@ const CustomNodeFlow = () => { ); }; -export default CustomNodeFlow; +export default () => ( + + + +); 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/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/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 4d7f061d..a9763f46 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -5,6 +5,7 @@ ## Patch changes - fix `ref` prop for `ReactFlow` and `Handle` component +- unify `Edge` and `Node` type handling ## 12.0.0-next.10 diff --git a/packages/react/src/components/EdgeWrapper/index.tsx b/packages/react/src/components/EdgeWrapper/index.tsx index 3baea708..30f432aa 100644 --- a/packages/react/src/components/EdgeWrapper/index.tsx +++ b/packages/react/src/components/EdgeWrapper/index.tsx @@ -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 d4474c00..f1f84cce 100644 --- a/packages/react/src/components/Handle/index.tsx +++ b/packages/react/src/components/Handle/index.tsx @@ -1,10 +1,10 @@ import { - memo, - forwardRef, 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'; 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/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/edges.ts b/packages/react/src/types/edges.ts index 602f2c34..21e4aa4e 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -98,10 +98,10 @@ export type EdgeTextProps = HTMLAttributes & * 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 c4365979..6f723237 100644 --- a/packages/react/src/types/general.ts +++ b/packages/react/src/types/general.ts @@ -10,18 +10,18 @@ import { SetCenter, FitBounds, XYPosition, - NodeProps, OnBeforeDeleteBase, Connection, } from '@xyflow/system'; -import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps } from '.'; +import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps, NodeProps } from '.'; // this is needed, to use generics + forwardRef declare module 'react' { - function forwardRef( - render: (props: P, ref: React.Ref) => React.ReactNode | null - ): (props: P & React.RefAttributes) => React.ReactNode | null; + // 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; @@ -34,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..ad78c5e6 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # @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 + ## 0.0.37 ## ⚠️ Breaking changes 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 @@