From a345b2670bcbe66d12f45f7a0bc2704a01b3f696 Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 24 Jan 2024 18:25:31 +0100 Subject: [PATCH 01/10] feat(react): infer node types from passed nodes --- .../react/src/examples/CustomNode/index.tsx | 2 +- .../react/src/examples/UseNodesData/index.tsx | 2 +- .../src/components/NodeWrapper/index.tsx | 8 +- .../src/components/NodesSelection/index.tsx | 12 +- .../src/components/StoreUpdater/index.tsx | 8 +- .../src/container/FlowRenderer/index.tsx | 14 +- .../react/src/container/GraphView/index.tsx | 18 +- .../src/container/NodeRenderer/index.tsx | 14 +- .../react/src/container/ReactFlow/index.tsx | 540 +++++++++--------- .../react/src/hooks/useNodesEdgesState.ts | 16 +- packages/react/src/hooks/useOnInitHandler.ts | 6 +- packages/react/src/hooks/useStore.ts | 4 +- packages/react/src/types/component-props.ts | 34 +- packages/react/src/types/nodes.ts | 14 +- packages/react/src/types/store.ts | 26 +- 15 files changed, 364 insertions(+), 354 deletions(-) diff --git a/examples/react/src/examples/CustomNode/index.tsx b/examples/react/src/examples/CustomNode/index.tsx index a8c0f55d..772d4ddd 100644 --- a/examples/react/src/examples/CustomNode/index.tsx +++ b/examples/react/src/examples/CustomNode/index.tsx @@ -44,7 +44,7 @@ const nodeTypes = { const CustomNodeFlow = () => { const [nodes, setNodes] = useState([]); - const onNodesChange: OnNodesChange = useCallback( + const onNodesChange: OnNodesChange = useCallback( (changes) => setNodes((nds) => { const nextNodes = applyNodeChanges(changes, nds); diff --git a/examples/react/src/examples/UseNodesData/index.tsx b/examples/react/src/examples/UseNodesData/index.tsx index d6b697a9..b9bc68f1 100644 --- a/examples/react/src/examples/UseNodesData/index.tsx +++ b/examples/react/src/examples/UseNodesData/index.tsx @@ -18,7 +18,7 @@ import UppercaseNode from './UppercaseNode'; export type TextNode = Node<{ text: string }, 'text'>; export type ResultNode = Node<{}, 'result'>; export type UppercaseNode = Node<{}, 'uppercase'>; -export type MyNode = Node<{ text: string }, 'text'> | Node<{}, 'result'> | Node<{}, 'uppercase'>; +export type MyNode = TextNode | ResultNode | UppercaseNode; const nodeTypes = { text: TextNode, diff --git a/packages/react/src/components/NodeWrapper/index.tsx b/packages/react/src/components/NodeWrapper/index.tsx index 1e796de1..017dd0f9 100644 --- a/packages/react/src/components/NodeWrapper/index.tsx +++ b/packages/react/src/components/NodeWrapper/index.tsx @@ -17,9 +17,9 @@ import { useDrag } from '../../hooks/useDrag'; import { useUpdateNodePositions } from '../../hooks/useUpdateNodePositions'; import { handleNodeClick } from '../Nodes/utils'; import { arrowKeyDiffs, builtinNodeTypes } from './utils'; -import type { NodeWrapperProps } from '../../types'; +import type { Node, NodeWrapperProps } from '../../types'; -export function NodeWrapper({ +export function NodeWrapper({ id, onClick, onMouseEnter, @@ -40,9 +40,9 @@ export function NodeWrapper({ nodeExtent, nodeOrigin, onError, -}: NodeWrapperProps) { +}: NodeWrapperProps) { const { node, positionAbsoluteX, positionAbsoluteY, zIndex, isParent } = useStore((s) => { - const node = s.nodeLookup.get(id)!; + const node = s.nodeLookup.get(id)! as NodeType; const positionAbsolute = nodeExtent ? clampPosition(node.computed?.positionAbsolute, nodeExtent) diff --git a/packages/react/src/components/NodesSelection/index.tsx b/packages/react/src/components/NodesSelection/index.tsx index f8aeb77c..8ba01979 100644 --- a/packages/react/src/components/NodesSelection/index.tsx +++ b/packages/react/src/components/NodesSelection/index.tsx @@ -14,8 +14,8 @@ import { useUpdateNodePositions } from '../../hooks/useUpdateNodePositions'; import { arrowKeyDiffs } from '../NodeWrapper/utils'; import type { Node, ReactFlowState } from '../../types'; -export type NodesSelectionProps = { - onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void; +export type NodesSelectionProps = { + onSelectionContextMenu?: (event: MouseEvent, nodes: NodeType[]) => void; noPanClassName?: string; disableKeyboardA11y: boolean; }; @@ -32,7 +32,11 @@ const selector = (s: ReactFlowState) => { }; }; -export function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y }: NodesSelectionProps) { +export function NodesSelection({ + onSelectionContextMenu, + noPanClassName, + disableKeyboardA11y, +}: NodesSelectionProps) { const store = useStoreApi(); const { width, height, transformString, userSelectionActive } = useStore(selector, shallow); const updatePositions = useUpdateNodePositions(); @@ -58,7 +62,7 @@ export function NodesSelection({ onSelectionContextMenu, noPanClassName, disable const onContextMenu = onSelectionContextMenu ? (event: MouseEvent) => { const selectedNodes = store.getState().nodes.filter((n) => n.selected); - onSelectionContextMenu(event, selectedNodes); + onSelectionContextMenu(event, selectedNodes as NodeType[]); } : undefined; diff --git a/packages/react/src/components/StoreUpdater/index.tsx b/packages/react/src/components/StoreUpdater/index.tsx index 3a12594c..6211ea59 100644 --- a/packages/react/src/components/StoreUpdater/index.tsx +++ b/packages/react/src/components/StoreUpdater/index.tsx @@ -68,7 +68,9 @@ const reactFlowFieldsToTrack = [ ] as const; type ReactFlowFieldsToTrack = (typeof reactFlowFieldsToTrack)[number]; -type StoreUpdaterProps = Pick & { rfId: string }; +type StoreUpdaterProps = Pick, ReactFlowFieldsToTrack> & { + rfId: string; +}; // rfId doesn't exist in ReactFlowProps, but it's one of the fields we want to update const fieldsToTrack = [...reactFlowFieldsToTrack, 'rfId'] as const; @@ -84,7 +86,7 @@ const selector = (s: ReactFlowState) => ({ reset: s.reset, }); -export function StoreUpdater(props: StoreUpdaterProps) { +export function StoreUpdater(props: StoreUpdaterProps) { const { setNodes, setEdges, @@ -106,7 +108,7 @@ export function StoreUpdater(props: StoreUpdaterProps) { }; }, []); - const previousFields = useRef>({ + const previousFields = useRef>>({ // these are values that are also passed directly to other components // than the StoreUpdater. We can reduce the number of setStore calls // by setting the same values here as prev fields. diff --git a/packages/react/src/container/FlowRenderer/index.tsx b/packages/react/src/container/FlowRenderer/index.tsx index 2670be70..6aa49a2a 100644 --- a/packages/react/src/container/FlowRenderer/index.tsx +++ b/packages/react/src/container/FlowRenderer/index.tsx @@ -7,10 +7,10 @@ import { GraphViewProps } from '../GraphView'; import { ZoomPane } from '../ZoomPane'; import { Pane } from '../Pane'; import { NodesSelection } from '../../components/NodesSelection'; -import type { ReactFlowState } from '../../types'; +import type { ReactFlowState, Node } from '../../types'; -export type FlowRendererProps = Omit< - GraphViewProps, +export type FlowRendererProps = Omit< + GraphViewProps, | 'snapToGrid' | 'nodeTypes' | 'edgeTypes' @@ -32,7 +32,7 @@ const selector = (s: ReactFlowState) => { return { nodesSelectionActive: s.nodesSelectionActive, userSelectionActive: s.userSelectionActive }; }; -const FlowRendererComponent = ({ +function FlowRendererComponent({ children, onPaneClick, onPaneMouseEnter, @@ -68,7 +68,7 @@ const FlowRendererComponent = ({ disableKeyboardA11y, onViewportChange, isControlledViewport, -}: FlowRendererProps) => { +}: FlowRendererProps) { const { nodesSelectionActive, userSelectionActive } = useStore(selector); const selectionKeyPressed = useKeyPress(selectionKeyCode); const panActivationKeyPressed = useKeyPress(panActivationKeyCode); @@ -125,8 +125,8 @@ const FlowRendererComponent = ({ ); -}; +} FlowRendererComponent.displayName = 'FlowRenderer'; -export const FlowRenderer = memo(FlowRendererComponent); +export const FlowRenderer = memo(FlowRendererComponent) as typeof FlowRendererComponent; diff --git a/packages/react/src/container/GraphView/index.tsx b/packages/react/src/container/GraphView/index.tsx index fd7ac277..0615258c 100644 --- a/packages/react/src/container/GraphView/index.tsx +++ b/packages/react/src/container/GraphView/index.tsx @@ -8,15 +8,15 @@ import { useOnInitHandler } from '../../hooks/useOnInitHandler'; import { useViewportSync } from '../../hooks/useViewportSync'; import { ConnectionLineWrapper } from '../../components/ConnectionLine'; import { useNodeOrEdgeTypesWarning } from './useNodeOrEdgeTypesWarning'; -import type { ReactFlowProps } from '../../types'; +import type { Node, ReactFlowProps } from '../../types'; -export type GraphViewProps = Omit< - ReactFlowProps, +export type GraphViewProps = Omit< + ReactFlowProps, 'onSelectionChange' | 'nodes' | 'edges' | 'onMove' | 'onMoveStart' | 'onMoveEnd' | 'elevateEdgesOnSelect' > & Required< Pick< - ReactFlowProps, + ReactFlowProps, | 'selectionKeyCode' | 'deleteKeyCode' | 'multiSelectionKeyCode' @@ -38,7 +38,7 @@ export type GraphViewProps = Omit< rfId: string; }; -function GraphViewComponent({ +function GraphViewComponent({ nodeTypes, edgeTypes, onInit, @@ -102,7 +102,7 @@ function GraphViewComponent({ rfId, viewport, onViewportChange, -}: GraphViewProps) { +}: GraphViewProps) { useNodeOrEdgeTypesWarning(nodeTypes); useNodeOrEdgeTypesWarning(edgeTypes); @@ -110,7 +110,7 @@ function GraphViewComponent({ useViewportSync(viewport); return ( - onPaneClick={onPaneClick} onPaneMouseEnter={onPaneMouseEnter} onPaneMouseMove={onPaneMouseMove} @@ -174,7 +174,7 @@ function GraphViewComponent({ />
- nodeTypes={nodeTypes} onNodeClick={onNodeClick} onNodeDoubleClick={onNodeDoubleClick} @@ -197,4 +197,4 @@ function GraphViewComponent({ GraphViewComponent.displayName = 'GraphView'; -export const GraphView = memo(GraphViewComponent); +export const GraphView = memo(GraphViewComponent) as typeof GraphViewComponent; diff --git a/packages/react/src/container/NodeRenderer/index.tsx b/packages/react/src/container/NodeRenderer/index.tsx index 11cab66f..39b20323 100644 --- a/packages/react/src/container/NodeRenderer/index.tsx +++ b/packages/react/src/container/NodeRenderer/index.tsx @@ -7,10 +7,10 @@ import { containerStyle } from '../../styles/utils'; import { GraphViewProps } from '../GraphView'; import { useResizeObserver } from './useResizeObserver'; import { NodeWrapper } from '../../components/NodeWrapper'; -import type { ReactFlowState } from '../../types'; +import type { Node, ReactFlowState } from '../../types'; -export type NodeRendererProps = Pick< - GraphViewProps, +export type NodeRendererProps = Pick< + GraphViewProps, | 'onNodeClick' | 'onNodeDoubleClick' | 'onNodeMouseEnter' @@ -35,7 +35,7 @@ const selector = (s: ReactFlowState) => ({ onError: s.onError, }); -const NodeRendererComponent = (props: NodeRendererProps) => { +function NodeRendererComponent(props: NodeRendererProps) { const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, onError } = useStore(selector, shallow); const nodeIds = useVisibleNodeIds(props.onlyRenderVisibleElements); const resizeObserver = useResizeObserver(); @@ -67,7 +67,7 @@ const NodeRendererComponent = (props: NodeRendererProps) => { // moved into `NodeComponentWrapper`. This ensures they are // memorized – so if `NodeRenderer` *has* to rerender, it only // needs to regenerate the list of nodes, nothing else. - key={nodeId} id={nodeId} nodeTypes={props.nodeTypes} @@ -94,8 +94,8 @@ const NodeRendererComponent = (props: NodeRendererProps) => { })}
); -}; +} NodeRendererComponent.displayName = 'NodeRenderer'; -export const NodeRenderer = memo(NodeRendererComponent); +export const NodeRenderer = memo(NodeRendererComponent) as typeof NodeRendererComponent; diff --git a/packages/react/src/container/ReactFlow/index.tsx b/packages/react/src/container/ReactFlow/index.tsx index d64af5ec..2516b4e7 100644 --- a/packages/react/src/container/ReactFlow/index.tsx +++ b/packages/react/src/container/ReactFlow/index.tsx @@ -1,4 +1,4 @@ -import { forwardRef, type CSSProperties } from 'react'; +import { ForwardedRef, forwardRef, type CSSProperties } from 'react'; import cc from 'classcat'; import { ConnectionLineType, @@ -17,7 +17,7 @@ import { StoreUpdater } from '../../components/StoreUpdater'; import { useColorModeClass } from '../../hooks/useColorModeClass'; import { GraphView } from '../GraphView'; import { Wrapper } from './Wrapper'; -import type { ReactFlowProps, ReactFlowRefType } from '../../types'; +import type { ReactFlowProps, ReactFlowRefType, Node } from '../../types'; export const initNodeOrigin: NodeOrigin = [0, 0]; const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 }; @@ -30,272 +30,276 @@ const wrapperStyle: CSSProperties = { zIndex: 0, }; -const ReactFlow = forwardRef( - ( - { - nodes, - edges, - defaultNodes, - defaultEdges, - className, - nodeTypes, - edgeTypes, - onNodeClick, - onEdgeClick, - onInit, - onMove, - onMoveStart, - onMoveEnd, - onConnect, - onConnectStart, - onConnectEnd, - onClickConnectStart, - onClickConnectEnd, - onNodeMouseEnter, - onNodeMouseMove, - onNodeMouseLeave, - onNodeContextMenu, - onNodeDoubleClick, - onNodeDragStart, - onNodeDrag, - onNodeDragStop, - onNodesDelete, - onEdgesDelete, - onDelete, - onSelectionChange, - onSelectionDragStart, - onSelectionDrag, - onSelectionDragStop, - onSelectionContextMenu, - onSelectionStart, - onSelectionEnd, - onBeforeDelete, - connectionMode, - connectionLineType = ConnectionLineType.Bezier, - connectionLineStyle, - connectionLineComponent, - connectionLineContainerStyle, - deleteKeyCode = 'Backspace', - selectionKeyCode = 'Shift', - selectionOnDrag = false, - selectionMode = SelectionMode.Full, - panActivationKeyCode = 'Space', - multiSelectionKeyCode = isMacOs() ? 'Meta' : 'Control', - zoomActivationKeyCode = isMacOs() ? 'Meta' : 'Control', - snapToGrid, - snapGrid, - onlyRenderVisibleElements = false, - selectNodesOnDrag, - nodesDraggable, - nodesConnectable, - nodesFocusable, - nodeOrigin = initNodeOrigin, - edgesFocusable, - edgesUpdatable, - elementsSelectable = true, - defaultViewport = initDefaultViewport, - minZoom = 0.5, - maxZoom = 2, - translateExtent = infiniteExtent, - preventScrolling = true, - nodeExtent, - defaultMarkerColor = '#b1b1b7', - zoomOnScroll = true, - zoomOnPinch = true, - panOnScroll = false, - panOnScrollSpeed = 0.5, - panOnScrollMode = PanOnScrollMode.Free, - zoomOnDoubleClick = true, - panOnDrag = true, - onPaneClick, - onPaneMouseEnter, - onPaneMouseMove, - onPaneMouseLeave, - onPaneScroll, - onPaneContextMenu, - children, - onEdgeUpdate, - onEdgeContextMenu, - onEdgeDoubleClick, - onEdgeMouseEnter, - onEdgeMouseMove, - onEdgeMouseLeave, - onEdgeUpdateStart, - onEdgeUpdateEnd, - edgeUpdaterRadius = 10, - onNodesChange, - onEdgesChange, - noDragClassName = 'nodrag', - noWheelClassName = 'nowheel', - noPanClassName = 'nopan', - fitView, - fitViewOptions, - connectOnClick, - attributionPosition, - proOptions, - defaultEdgeOptions, - elevateNodesOnSelect, - elevateEdgesOnSelect, - disableKeyboardA11y = false, - autoPanOnConnect, - autoPanOnNodeDrag, - connectionRadius, - isValidConnection, - onError, - style, - id, - nodeDragThreshold, - viewport, - onViewportChange, - width, - height, - colorMode = 'light', - ...rest - }, - ref - ) => { - const rfId = id || '1'; - const colorModeClassName = useColorModeClass(colorMode); +declare module 'react' { + function forwardRef>( + render: (props: P, ref: React.Ref) => React.ReactNode | null + ): (props: P & React.RefAttributes) => React.ReactNode | null; +} - return ( -
- - - - - {children} - - - -
- ); - } -); +function ReactFlow( + { + nodes, + edges, + defaultNodes, + defaultEdges, + className, + nodeTypes, + edgeTypes, + onNodeClick, + onEdgeClick, + onInit, + onMove, + onMoveStart, + onMoveEnd, + onConnect, + onConnectStart, + onConnectEnd, + onClickConnectStart, + onClickConnectEnd, + onNodeMouseEnter, + onNodeMouseMove, + onNodeMouseLeave, + onNodeContextMenu, + onNodeDoubleClick, + onNodeDragStart, + onNodeDrag, + onNodeDragStop, + onNodesDelete, + onEdgesDelete, + onDelete, + onSelectionChange, + onSelectionDragStart, + onSelectionDrag, + onSelectionDragStop, + onSelectionContextMenu, + onSelectionStart, + onSelectionEnd, + onBeforeDelete, + connectionMode, + connectionLineType = ConnectionLineType.Bezier, + connectionLineStyle, + connectionLineComponent, + connectionLineContainerStyle, + deleteKeyCode = 'Backspace', + selectionKeyCode = 'Shift', + selectionOnDrag = false, + selectionMode = SelectionMode.Full, + panActivationKeyCode = 'Space', + multiSelectionKeyCode = isMacOs() ? 'Meta' : 'Control', + zoomActivationKeyCode = isMacOs() ? 'Meta' : 'Control', + snapToGrid, + snapGrid, + onlyRenderVisibleElements = false, + selectNodesOnDrag, + nodesDraggable, + nodesConnectable, + nodesFocusable, + nodeOrigin = initNodeOrigin, + edgesFocusable, + edgesUpdatable, + elementsSelectable = true, + defaultViewport = initDefaultViewport, + minZoom = 0.5, + maxZoom = 2, + translateExtent = infiniteExtent, + preventScrolling = true, + nodeExtent, + defaultMarkerColor = '#b1b1b7', + zoomOnScroll = true, + zoomOnPinch = true, + panOnScroll = false, + panOnScrollSpeed = 0.5, + panOnScrollMode = PanOnScrollMode.Free, + zoomOnDoubleClick = true, + panOnDrag = true, + onPaneClick, + onPaneMouseEnter, + onPaneMouseMove, + onPaneMouseLeave, + onPaneScroll, + onPaneContextMenu, + children, + onEdgeUpdate, + onEdgeContextMenu, + onEdgeDoubleClick, + onEdgeMouseEnter, + onEdgeMouseMove, + onEdgeMouseLeave, + onEdgeUpdateStart, + onEdgeUpdateEnd, + edgeUpdaterRadius = 10, + onNodesChange, + onEdgesChange, + noDragClassName = 'nodrag', + noWheelClassName = 'nowheel', + noPanClassName = 'nopan', + fitView, + fitViewOptions, + connectOnClick, + attributionPosition, + proOptions, + defaultEdgeOptions, + elevateNodesOnSelect, + elevateEdgesOnSelect, + disableKeyboardA11y = false, + autoPanOnConnect, + autoPanOnNodeDrag, + connectionRadius, + isValidConnection, + onError, + style, + id, + nodeDragThreshold, + viewport, + onViewportChange, + width, + height, + colorMode = 'light', + ...rest + }: ReactFlowProps, + ref: ForwardedRef +) { + const rfId = id || '1'; + const colorModeClassName = useColorModeClass(colorMode); -ReactFlow.displayName = 'ReactFlow'; + return ( +
+ + + onInit={onInit} + onNodeClick={onNodeClick} + onEdgeClick={onEdgeClick} + onNodeMouseEnter={onNodeMouseEnter} + onNodeMouseMove={onNodeMouseMove} + onNodeMouseLeave={onNodeMouseLeave} + onNodeContextMenu={onNodeContextMenu} + onNodeDoubleClick={onNodeDoubleClick} + nodeTypes={nodeTypes} + edgeTypes={edgeTypes} + connectionLineType={connectionLineType} + connectionLineStyle={connectionLineStyle} + connectionLineComponent={connectionLineComponent} + connectionLineContainerStyle={connectionLineContainerStyle} + selectionKeyCode={selectionKeyCode} + selectionOnDrag={selectionOnDrag} + selectionMode={selectionMode} + deleteKeyCode={deleteKeyCode} + multiSelectionKeyCode={multiSelectionKeyCode} + panActivationKeyCode={panActivationKeyCode} + zoomActivationKeyCode={zoomActivationKeyCode} + onlyRenderVisibleElements={onlyRenderVisibleElements} + defaultViewport={defaultViewport} + translateExtent={translateExtent} + minZoom={minZoom} + maxZoom={maxZoom} + preventScrolling={preventScrolling} + zoomOnScroll={zoomOnScroll} + zoomOnPinch={zoomOnPinch} + zoomOnDoubleClick={zoomOnDoubleClick} + panOnScroll={panOnScroll} + panOnScrollSpeed={panOnScrollSpeed} + panOnScrollMode={panOnScrollMode} + panOnDrag={panOnDrag} + onPaneClick={onPaneClick} + onPaneMouseEnter={onPaneMouseEnter} + onPaneMouseMove={onPaneMouseMove} + onPaneMouseLeave={onPaneMouseLeave} + onPaneScroll={onPaneScroll} + onPaneContextMenu={onPaneContextMenu} + onSelectionContextMenu={onSelectionContextMenu} + onSelectionStart={onSelectionStart} + onSelectionEnd={onSelectionEnd} + onEdgeUpdate={onEdgeUpdate} + onEdgeContextMenu={onEdgeContextMenu} + onEdgeDoubleClick={onEdgeDoubleClick} + onEdgeMouseEnter={onEdgeMouseEnter} + onEdgeMouseMove={onEdgeMouseMove} + onEdgeMouseLeave={onEdgeMouseLeave} + onEdgeUpdateStart={onEdgeUpdateStart} + onEdgeUpdateEnd={onEdgeUpdateEnd} + edgeUpdaterRadius={edgeUpdaterRadius} + defaultMarkerColor={defaultMarkerColor} + noDragClassName={noDragClassName} + noWheelClassName={noWheelClassName} + noPanClassName={noPanClassName} + rfId={rfId} + disableKeyboardA11y={disableKeyboardA11y} + nodeOrigin={nodeOrigin} + nodeExtent={nodeExtent} + viewport={viewport} + onViewportChange={onViewportChange} + /> + + + {children} + + + +
+ ); +} -export default ReactFlow; +export default forwardRef(ReactFlow) as ( + props: ReactFlowProps & { ref?: React.ForwardedRef } +) => ReturnType; diff --git a/packages/react/src/hooks/useNodesEdgesState.ts b/packages/react/src/hooks/useNodesEdgesState.ts index 766ee049..fae87c02 100644 --- a/packages/react/src/hooks/useNodesEdgesState.ts +++ b/packages/react/src/hooks/useNodesEdgesState.ts @@ -1,7 +1,7 @@ import { useState, useCallback, type Dispatch, type SetStateAction } from 'react'; import { applyNodeChanges, applyEdgeChanges } from '../utils/changes'; -import type { Node, NodeChange, Edge, EdgeChange } from '../types'; +import type { Node, Edge, OnNodesChange, OnEdgesChange } from '../types'; /** * Hook for managing the state of nodes - should only be used for prototyping / simple use cases. @@ -10,12 +10,12 @@ import type { Node, NodeChange, Edge, EdgeChange } from '../types'; * @param initialNodes * @returns an array [nodes, setNodes, onNodesChange] */ -export function useNodesState( +export function useNodesState( initialNodes: NodeType[] -): [NodeType[], Dispatch>, (changes: NodeChange[]) => void] { +): [NodeType[], Dispatch>, OnNodesChange] { const [nodes, setNodes] = useState(initialNodes); - const onNodesChange = useCallback( - (changes: NodeChange[]) => setNodes((nds) => applyNodeChanges(changes, nds)), + const onNodesChange: OnNodesChange = useCallback( + (changes) => setNodes((nds) => applyNodeChanges(changes, nds)), [] ); @@ -31,10 +31,10 @@ export function useNodesState( */ export function useEdgesState( initialEdges: EdgeType[] -): [EdgeType[], Dispatch>, (changes: EdgeChange[]) => void] { +): [EdgeType[], Dispatch>, OnEdgesChange] { const [edges, setEdges] = useState(initialEdges); - const onEdgesChange = useCallback( - (changes: EdgeChange[]) => setEdges((eds) => applyEdgeChanges(changes, eds)), + const onEdgesChange: OnEdgesChange = useCallback( + (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)), [] ); diff --git a/packages/react/src/hooks/useOnInitHandler.ts b/packages/react/src/hooks/useOnInitHandler.ts index 5b677c1a..782b0379 100644 --- a/packages/react/src/hooks/useOnInitHandler.ts +++ b/packages/react/src/hooks/useOnInitHandler.ts @@ -1,15 +1,15 @@ import { useEffect, useRef } from 'react'; import { useReactFlow } from './useReactFlow'; -import type { OnInit } from '../types'; +import type { OnInit, Node } from '../types'; /** * Hook for calling onInit handler. * * @internal */ -export function useOnInitHandler(onInit: OnInit | undefined) { - const rfInstance = useReactFlow(); +export function useOnInitHandler(onInit: OnInit | undefined) { + const rfInstance = useReactFlow(); const isInitialized = useRef(false); useEffect(() => { diff --git a/packages/react/src/hooks/useStore.ts b/packages/react/src/hooks/useStore.ts index 96587161..9f7f8255 100644 --- a/packages/react/src/hooks/useStore.ts +++ b/packages/react/src/hooks/useStore.ts @@ -31,7 +31,7 @@ function useStore( return useZustandStore(store, selector, equalityFn); } -const useStoreApi = () => { +function useStoreApi() { const store = useContext(StoreContext); if (store === null) { @@ -47,6 +47,6 @@ const useStoreApi = () => { }), [store] ); -}; +} export { useStore, useStoreApi }; diff --git a/packages/react/src/types/component-props.ts b/packages/react/src/types/component-props.ts index 6018e1f5..f5f854ab 100644 --- a/packages/react/src/types/component-props.ts +++ b/packages/react/src/types/component-props.ts @@ -50,7 +50,7 @@ import type { * ReactFlow component props. * @public */ -export interface ReactFlowProps extends Omit, 'onError'> { +export interface ReactFlowProps extends Omit, 'onError'> { /** An array of nodes to render in a controlled flow. * @example * const nodes = [ @@ -62,7 +62,7 @@ export interface ReactFlowProps extends Omit, 'on * } * ]; */ - nodes?: Node[]; + nodes?: NodeType[]; /** An array of edges to render in a controlled flow. * @example * const edges = [ @@ -75,7 +75,7 @@ export interface ReactFlowProps extends Omit, 'on */ edges?: Edge[]; /** The initial nodes to render in an uncontrolled flow. */ - defaultNodes?: Node[]; + defaultNodes?: NodeType[]; /** The initial edges to render in an uncontrolled flow. */ defaultEdges?: Edge[]; /** Defaults to be applied to all new edges that are added to the flow. @@ -99,23 +99,23 @@ export interface ReactFlowProps extends Omit, 'on */ defaultEdgeOptions?: DefaultEdgeOptions; /** This event handler is called when a user clicks on a node */ - onNodeClick?: NodeMouseHandler; + onNodeClick?: NodeMouseHandler; /** This event handler is called when a user double clicks on a node */ - onNodeDoubleClick?: NodeMouseHandler; + onNodeDoubleClick?: NodeMouseHandler; /** This event handler is called when mouse of a user enters a node */ - onNodeMouseEnter?: NodeMouseHandler; + onNodeMouseEnter?: NodeMouseHandler; /** This event handler is called when mouse of a user moves over a node */ - onNodeMouseMove?: NodeMouseHandler; + onNodeMouseMove?: NodeMouseHandler; /** This event handler is called when mouse of a user leaves a node */ - onNodeMouseLeave?: NodeMouseHandler; + onNodeMouseLeave?: NodeMouseHandler; /** This event handler is called when a user right clicks on a node */ - onNodeContextMenu?: NodeMouseHandler; + onNodeContextMenu?: NodeMouseHandler; /** This event handler is called when a user starts to drag a node */ - onNodeDragStart?: OnNodeDrag; + onNodeDragStart?: OnNodeDrag; /** This event handler is called when a user drags a node */ - onNodeDrag?: OnNodeDrag; + onNodeDrag?: OnNodeDrag; /** This event handler is called when a user stops dragging a node */ - onNodeDragStop?: OnNodeDrag; + onNodeDragStop?: OnNodeDrag; /** This event handler is called when a user clicks on an edge */ onEdgeClick?: (event: ReactMouseEvent, edge: Edge) => void; /** This event handler is called when a user right clicks on an edge */ @@ -147,7 +147,7 @@ export interface ReactFlowProps extends Omit, 'on * * return () */ - onNodesChange?: OnNodesChange; + onNodesChange?: OnNodesChange; /** This event handler is called when a Edge is updated * @example // Use EdgesState hook to create edges and get onEdgesChange handler * import ReactFlow, { useEdgesState } from '@xyflow/react'; @@ -166,7 +166,7 @@ export interface ReactFlowProps extends Omit, 'on */ onEdgesChange?: OnEdgesChange; /** This event handler gets called when a Node is deleted */ - onNodesDelete?: OnNodesDelete; + onNodesDelete?: OnNodesDelete; /** This event handler gets called when a Edge is deleted */ onEdgesDelete?: OnEdgesDelete; /** This event handler gets called when a Node or Edge is deleted */ @@ -179,7 +179,7 @@ export interface ReactFlowProps extends Omit, 'on onSelectionDragStop?: SelectionDragHandler; onSelectionStart?: (event: ReactMouseEvent) => void; onSelectionEnd?: (event: ReactMouseEvent) => void; - onSelectionContextMenu?: (event: ReactMouseEvent, nodes: Node[]) => void; + onSelectionContextMenu?: (event: ReactMouseEvent, nodes: NodeType[]) => void; /** When a connection line is completed and two nodes are connected by the user, this event fires with the new connection. * * You can use the addEdge utility to convert the connection to a complete edge. @@ -201,7 +201,7 @@ export interface ReactFlowProps extends Omit, 'on onClickConnectStart?: OnConnectStart; onClickConnectEnd?: OnConnectEnd; /** This event handler gets called when a flow has finished initializing */ - onInit?: OnInit; + onInit?: OnInit; /** This event handler is called while the user is either panning or zooming the viewport. */ onMove?: OnMove; /** This event handler gets called when a user starts to pan or zoom the viewport */ @@ -223,7 +223,7 @@ export interface ReactFlowProps extends Omit, 'on /** This event handler gets called when mouse leaves the pane */ onPaneMouseLeave?: (event: ReactMouseEvent) => void; /** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */ - onBeforeDelete?: OnBeforeDelete; + onBeforeDelete?: OnBeforeDelete; /** Custom node types to be available in a flow. * * React Flow matches a node's type to a component in the nodeTypes object. diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts index 612c1d50..9b28c78f 100644 --- a/packages/react/src/types/nodes.ts +++ b/packages/react/src/types/nodes.ts @@ -26,18 +26,18 @@ export type OnNodeDrag = ( nodes: NodeType[] ) => void; -export type NodeWrapperProps = { +export type NodeWrapperProps = { id: string; nodesConnectable: boolean; elementsSelectable: boolean; nodesDraggable: boolean; nodesFocusable: boolean; - onClick?: NodeMouseHandler; - onDoubleClick?: NodeMouseHandler; - onMouseEnter?: NodeMouseHandler; - onMouseMove?: NodeMouseHandler; - onMouseLeave?: NodeMouseHandler; - onContextMenu?: NodeMouseHandler; + onClick?: NodeMouseHandler; + onDoubleClick?: NodeMouseHandler; + onMouseEnter?: NodeMouseHandler; + onMouseMove?: NodeMouseHandler; + onMouseLeave?: NodeMouseHandler; + onContextMenu?: NodeMouseHandler; resizeObserver: ResizeObserver | null; noDragClassName: string; noPanClassName: string; diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index b3e0449e..27482dba 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -45,17 +45,17 @@ import type { OnBeforeDelete, } from '.'; -export type ReactFlowStore = { +export type ReactFlowStore = { rfId: string; width: number; height: number; transform: Transform; - nodes: Node[]; - nodeLookup: NodeLookup; + nodes: NodeType[]; + nodeLookup: NodeLookup; edges: Edge[]; edgeLookup: EdgeLookup; connectionLookup: ConnectionLookup; - onNodesChange: OnNodesChange | null; + onNodesChange: OnNodesChange | null; onEdgesChange: OnEdgesChange | null; hasDefaultNodes: boolean; hasDefaultEdges: boolean; @@ -98,9 +98,9 @@ export type ReactFlowStore = { connectionEndHandle: ConnectingHandle | null; connectionClickStartHandle: ConnectingHandle | null; - onNodeDragStart?: OnNodeDrag; - onNodeDrag?: OnNodeDrag; - onNodeDragStop?: OnNodeDrag; + onNodeDragStart?: OnNodeDrag; + onNodeDrag?: OnNodeDrag; + onNodeDragStop?: OnNodeDrag; onSelectionDragStart?: OnSelectionDrag; onSelectionDrag?: OnSelectionDrag; @@ -147,10 +147,10 @@ export type ReactFlowStore = { lib: string; }; -export type ReactFlowActions = { - setNodes: (nodes: Node[]) => void; +export type ReactFlowActions = { + setNodes: (nodes: NodeType[]) => void; setEdges: (edges: Edge[]) => void; - setDefaultNodesAndEdges: (nodes?: Node[], edges?: Edge[]) => void; + setDefaultNodesAndEdges: (nodes?: NodeType[], edges?: Edge[]) => void; updateNodeDimensions: (updates: Map) => void; updateNodePositions: UpdateNodePositions; resetSelectedElements: () => void; @@ -164,9 +164,9 @@ export type ReactFlowActions = { cancelConnection: () => void; updateConnection: UpdateConnection; reset: () => void; - triggerNodeChanges: (changes: NodeChange[]) => void; + triggerNodeChanges: (changes: NodeChange[]) => void; panBy: PanBy; - fitView: (nodes: Node[], options?: FitViewOptions) => boolean; + fitView: (nodes: NodeType[], options?: FitViewOptions) => boolean; }; -export type ReactFlowState = ReactFlowStore & ReactFlowActions; +export type ReactFlowState = ReactFlowStore & ReactFlowActions; From cccfc21114a43917c5b825dc2d649903ba4994c0 Mon Sep 17 00:00:00 2001 From: moklick Date: Fri, 26 Jan 2024 09:57:19 +0100 Subject: [PATCH 02/10] refactor(react): minimap generic node type --- .../react/src/examples/CustomNode/index.tsx | 7 ++-- .../react/src/examples/UseNodesData/index.tsx | 2 +- .../additional-components/MiniMap/MiniMap.tsx | 12 +++---- .../MiniMap/MiniMapNodes.tsx | 33 ++++++++++--------- packages/react/src/types/nodes.ts | 2 ++ 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/examples/react/src/examples/CustomNode/index.tsx b/examples/react/src/examples/CustomNode/index.tsx index 772d4ddd..32bf34a0 100644 --- a/examples/react/src/examples/CustomNode/index.tsx +++ b/examples/react/src/examples/CustomNode/index.tsx @@ -16,6 +16,7 @@ import { OnNodesChange, OnConnect, OnBeforeDelete, + BuiltInNode, } from '@xyflow/react'; import ColorSelectorNode from './ColorSelectorNode'; @@ -24,7 +25,7 @@ export type ColorSelectorNode = Node< { color: string; onChange: (event: ChangeEvent) => void }, 'selectorNode' >; -export type MyNode = Node | ColorSelectorNode; +export type MyNode = BuiltInNode | ColorSelectorNode; const onInit: OnInit = (reactFlowInstance) => { console.log('flow loaded:', reactFlowInstance); @@ -61,7 +62,7 @@ const CustomNodeFlow = () => { const onChange = (event: ChangeEvent) => { setNodes((nds) => nds.map((node) => { - if (node.id !== '2') { + if (node.id !== '2' || node.type !== 'selectorNode') { return node; } @@ -164,7 +165,7 @@ const CustomNodeFlow = () => { maxZoom={2} onBeforeDelete={onBeforeDelete} > - nodeStrokeColor={(n: MyNode): string => { if (n.type === 'input') return '#0041d0'; if (n.type === 'selectorNode') return bgColor; diff --git a/examples/react/src/examples/UseNodesData/index.tsx b/examples/react/src/examples/UseNodesData/index.tsx index d99ddb43..b9bc68f1 100644 --- a/examples/react/src/examples/UseNodesData/index.tsx +++ b/examples/react/src/examples/UseNodesData/index.tsx @@ -18,7 +18,7 @@ import UppercaseNode from './UppercaseNode'; export type TextNode = Node<{ text: string }, 'text'>; export type ResultNode = Node<{}, 'result'>; export type UppercaseNode = Node<{}, 'uppercase'>; -export type MyNode = Node | TextNode | ResultNode | UppercaseNode; +export type MyNode = TextNode | ResultNode | UppercaseNode; const nodeTypes = { text: TextNode, diff --git a/packages/react/src/additional-components/MiniMap/MiniMap.tsx b/packages/react/src/additional-components/MiniMap/MiniMap.tsx index cd3f64ae..60caca1b 100644 --- a/packages/react/src/additional-components/MiniMap/MiniMap.tsx +++ b/packages/react/src/additional-components/MiniMap/MiniMap.tsx @@ -7,7 +7,7 @@ import { getNodesBounds, getBoundsOfRects, XYMinimap, type Rect, type XYMinimapI import { useStore, useStoreApi } from '../../hooks/useStore'; import { Panel } from '../../components/Panel'; -import type { ReactFlowState } from '../../types'; +import type { ReactFlowState, Node } from '../../types'; import MiniMapNodes from './MiniMapNodes'; import type { MiniMapProps } from './types'; @@ -38,7 +38,7 @@ const selector = (s: ReactFlowState) => { const ARIA_LABEL_KEY = 'react-flow__minimap-desc'; -function MiniMapComponent({ +function MiniMapComponent({ style, className, nodeStrokeColor, @@ -61,7 +61,7 @@ function MiniMapComponent({ inversePan, zoomStep = 10, offsetScale = 5, -}: MiniMapProps) { +}: MiniMapProps) { const store = useStoreApi(); const svg = useRef(null); const { boundingRect, viewBB, rfId, panZoom, translateExtent, flowWidth, flowHeight } = useStore(selector, shallow); @@ -119,7 +119,7 @@ function MiniMapComponent({ const onSvgNodeClick = onNodeClick ? useCallback((event: MouseEvent, nodeId: string) => { - const node = store.getState().nodeLookup.get(nodeId)!; + const node = store.getState().nodeLookup.get(nodeId)! as NodeType; onNodeClick(event, node); }, []) : undefined; @@ -149,7 +149,7 @@ function MiniMapComponent({ onClick={onSvgClick} > {ariaLabel && {ariaLabel}} - onClick={onSvgNodeClick} nodeColor={nodeColor} nodeStrokeColor={nodeStrokeColor} @@ -174,4 +174,4 @@ function MiniMapComponent({ MiniMapComponent.displayName = 'MiniMap'; -export const MiniMap = memo(MiniMapComponent); +export const MiniMap = memo(MiniMapComponent) as typeof MiniMapComponent; diff --git a/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx b/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx index 63851aad..0dfb8fff 100644 --- a/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx +++ b/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx @@ -6,16 +6,17 @@ import { shallow } from 'zustand/shallow'; import { useStore } from '../../hooks/useStore'; import { MiniMapNode } from './MiniMapNode'; -import type { ReactFlowState } from '../../types'; +import type { ReactFlowState, Node } from '../../types'; import type { MiniMapNodes as MiniMapNodesProps, GetMiniMapNodeAttribute, MiniMapNodeProps } from './types'; declare const window: any; const selector = (s: ReactFlowState) => s.nodeOrigin; const selectorNodeIds = (s: ReactFlowState) => s.nodes.map((node) => node.id); -const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func); +const getAttrFunction = (func: any): GetMiniMapNodeAttribute => + func instanceof Function ? func : () => func; -function MiniMapNodes({ +function MiniMapNodes({ nodeStrokeColor, nodeColor, nodeClassName = '', @@ -25,12 +26,12 @@ function MiniMapNodes({ // a component properly. nodeComponent: NodeComponent = MiniMapNode, onClick, -}: MiniMapNodesProps) { +}: MiniMapNodesProps) { const nodeIds = useStore(selectorNodeIds, shallow); const nodeOrigin = useStore(selector); - const nodeColorFunc = getAttrFunction(nodeColor); - const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor); - const nodeClassNameFunc = getAttrFunction(nodeClassName); + const nodeColorFunc = getAttrFunction(nodeColor); + const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor); + const nodeClassNameFunc = getAttrFunction(nodeClassName); const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision'; @@ -42,7 +43,7 @@ function MiniMapNodes({ // minimize the cost of updates when individual nodes change. // // For more details, see a similar commit in `NodeRenderer/index.tsx`. - key={nodeId} id={nodeId} nodeOrigin={nodeOrigin} @@ -60,7 +61,7 @@ function MiniMapNodes({ ); } -const NodeComponentWrapper = memo(function NodeComponentWrapper({ +function NodeComponentWrapperInner({ id, nodeOrigin, nodeColorFunc, @@ -74,9 +75,9 @@ const NodeComponentWrapper = memo(function NodeComponentWrapper({ }: { id: string; nodeOrigin: NodeOrigin; - nodeColorFunc: GetMiniMapNodeAttribute; - nodeStrokeColorFunc: GetMiniMapNodeAttribute; - nodeClassNameFunc: GetMiniMapNodeAttribute; + nodeColorFunc: GetMiniMapNodeAttribute; + nodeStrokeColorFunc: GetMiniMapNodeAttribute; + nodeClassNameFunc: GetMiniMapNodeAttribute; nodeBorderRadius: number; nodeStrokeWidth?: number; NodeComponent: ComponentType; @@ -84,7 +85,7 @@ const NodeComponentWrapper = memo(function NodeComponentWrapper({ shapeRendering: string; }) { const { node, x, y } = useStore((s) => { - const node = s.nodeLookup.get(id); + const node = s.nodeLookup.get(id) as NodeType; const { x, y } = getNodePositionWithOrigin(node, node?.origin || nodeOrigin).positionAbsolute; return { @@ -115,6 +116,8 @@ const NodeComponentWrapper = memo(function NodeComponentWrapper({ id={node.id} /> ); -}); +} -export default memo(MiniMapNodes); +const NodeComponentWrapper = memo(NodeComponentWrapperInner) as typeof NodeComponentWrapperInner; + +export default memo(MiniMapNodes) as typeof MiniMapNodes; diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts index 9b28c78f..9ef7b9c3 100644 --- a/packages/react/src/types/nodes.ts +++ b/packages/react/src/types/nodes.ts @@ -48,3 +48,5 @@ export type NodeWrapperProps = { nodeOrigin: NodeOrigin; onError?: OnError; }; + +export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>; From f14480dc89922b4e31fe17847780b4921df009b4 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 17 Feb 2024 12:24:38 +0100 Subject: [PATCH 03/10] chore(useStore): cleanup --- packages/react/src/hooks/useStore.ts | 6 ++---- packages/react/src/types/store.ts | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/react/src/hooks/useStore.ts b/packages/react/src/hooks/useStore.ts index 9f7f8255..a80217a2 100644 --- a/packages/react/src/hooks/useStore.ts +++ b/packages/react/src/hooks/useStore.ts @@ -1,6 +1,5 @@ import { useContext, useMemo } from 'react'; import { useStoreWithEqualityFn as useZustandStore } from 'zustand/traditional'; -import type { StoreApi } from 'zustand'; import { errorMessages } from '@xyflow/system'; import StoreContext from '../contexts/RFStoreContext'; @@ -8,8 +7,6 @@ import type { ReactFlowState } from '../types'; const zustandErrorMessage = errorMessages['error001'](); -type ExtractState = StoreApi extends { getState: () => infer T } ? T : never; - /** * Hook for accessing the internal store. Should only be used in rare cases. * @@ -18,7 +15,8 @@ type ExtractState = StoreApi extends { getState: () => infer T } * @param equalityFn * @returns The selected state slice */ -function useStore( + +function useStore( selector: (state: ReactFlowState) => StateSlice, equalityFn?: (a: StateSlice, b: StateSlice) => boolean ) { diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index 27482dba..38e3fbe9 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -45,7 +45,7 @@ import type { OnBeforeDelete, } from '.'; -export type ReactFlowStore = { +export type ReactFlowStore = { rfId: string; width: number; height: number; From 776f6ce0b784973db0a35dd4f13ffc71c5a90a21 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 17 Feb 2024 14:35:20 +0100 Subject: [PATCH 04/10] chore(useStore): add tsdoc example --- packages/react/src/hooks/useStore.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react/src/hooks/useStore.ts b/packages/react/src/hooks/useStore.ts index a80217a2..9a3a5e6b 100644 --- a/packages/react/src/hooks/useStore.ts +++ b/packages/react/src/hooks/useStore.ts @@ -14,8 +14,11 @@ const zustandErrorMessage = errorMessages['error001'](); * @param selector * @param equalityFn * @returns The selected state slice + * + * @example + * const nodes = useStore((state: ReactFlowState) => state.nodes); + * */ - function useStore( selector: (state: ReactFlowState) => StateSlice, equalityFn?: (a: StateSlice, b: StateSlice) => boolean From ad7403cc75ac5884830599f6be7ae66b73132d51 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 17 Feb 2024 15:45:10 +0100 Subject: [PATCH 05/10] refactor(types): use same structure for nodes and edges --- .../EdgeWrapper/EdgeUpdateAnchors.tsx | 14 ++--- .../src/components/EdgeWrapper/index.tsx | 10 ++-- .../src/components/StoreUpdater/index.tsx | 11 ++-- .../src/container/EdgeRenderer/index.tsx | 14 ++--- .../src/container/FlowRenderer/index.tsx | 4 +- .../react/src/container/GraphView/index.tsx | 14 ++--- .../react/src/container/ReactFlow/index.tsx | 18 ++----- packages/react/src/hooks/useEdges.ts | 4 +- packages/react/src/hooks/useOnInitHandler.ts | 8 +-- packages/react/src/index.ts | 1 - packages/react/src/types/component-props.ts | 43 +++++++-------- packages/react/src/types/edges.ts | 53 ++++++++++--------- packages/react/src/types/general.ts | 10 +++- packages/react/src/types/nodes.ts | 9 ++-- packages/react/src/types/store.ts | 28 +++++----- .../src/lib/container/SvelteFlow/types.ts | 8 +-- packages/svelte/src/lib/hooks/useNodesData.ts | 2 +- packages/svelte/src/lib/index.ts | 1 - .../svelte/src/lib/store/initial-store.ts | 4 +- packages/svelte/src/lib/types/edges.ts | 41 ++++++++------ packages/svelte/src/lib/types/general.ts | 4 ++ packages/svelte/src/lib/types/nodes.ts | 4 +- packages/system/src/types/edges.ts | 7 ++- packages/system/src/types/nodes.ts | 11 ++-- 24 files changed, 176 insertions(+), 147 deletions(-) diff --git a/packages/react/src/components/EdgeWrapper/EdgeUpdateAnchors.tsx b/packages/react/src/components/EdgeWrapper/EdgeUpdateAnchors.tsx index db56017c..c84c2d3b 100644 --- a/packages/react/src/components/EdgeWrapper/EdgeUpdateAnchors.tsx +++ b/packages/react/src/components/EdgeWrapper/EdgeUpdateAnchors.tsx @@ -5,20 +5,20 @@ import { EdgeAnchor } from '../Edges/EdgeAnchor'; import type { EdgeWrapperProps, Edge } from '../../types/edges'; import { useStoreApi } from '../../hooks/useStore'; -type EdgeUpdateAnchorsProps = { - edge: Edge; +type EdgeUpdateAnchorsProps = { + edge: EdgeType; isUpdatable: boolean | 'source' | 'target'; edgeUpdaterRadius: EdgeWrapperProps['edgeUpdaterRadius']; sourceHandleId: Edge['sourceHandle']; targetHandleId: Edge['targetHandle']; - onEdgeUpdate: EdgeWrapperProps['onEdgeUpdate']; - onEdgeUpdateStart: EdgeWrapperProps['onEdgeUpdateStart']; - onEdgeUpdateEnd: EdgeWrapperProps['onEdgeUpdateEnd']; + onEdgeUpdate: EdgeWrapperProps['onEdgeUpdate']; + onEdgeUpdateStart: EdgeWrapperProps['onEdgeUpdateStart']; + onEdgeUpdateEnd: EdgeWrapperProps['onEdgeUpdateEnd']; setUpdateHover: (hover: boolean) => void; setUpdating: (updating: boolean) => void; } & EdgePosition; -export function EdgeUpdateAnchors({ +export function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, @@ -35,7 +35,7 @@ export function EdgeUpdateAnchors({ onEdgeUpdateEnd, setUpdating, setUpdateHover, -}: EdgeUpdateAnchorsProps) { +}: EdgeUpdateAnchorsProps) { const store = useStoreApi(); const handleEdgeUpdater = (event: React.MouseEvent, isSourceHandle: boolean) => { diff --git a/packages/react/src/components/EdgeWrapper/index.tsx b/packages/react/src/components/EdgeWrapper/index.tsx index 28323a19..b1fdb4a8 100644 --- a/packages/react/src/components/EdgeWrapper/index.tsx +++ b/packages/react/src/components/EdgeWrapper/index.tsx @@ -13,9 +13,9 @@ import { useStoreApi, useStore } from '../../hooks/useStore'; import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions'; import { builtinEdgeTypes, nullPosition } from './utils'; import { EdgeUpdateAnchors } from './EdgeUpdateAnchors'; -import type { EdgeWrapperProps } from '../../types'; +import type { Edge, EdgeWrapperProps } from '../../types'; -export function EdgeWrapper({ +export function EdgeWrapper({ id, edgesFocusable, edgesUpdatable, @@ -34,8 +34,8 @@ export function EdgeWrapper({ edgeTypes, noPanClassName, onError, -}: EdgeWrapperProps): JSX.Element | null { - let edge = useStore((s) => s.edgeLookup.get(id)!); +}: EdgeWrapperProps): JSX.Element | null { + let edge = useStore((s) => s.edgeLookup.get(id)!) as EdgeType; const defaultEdgeOptions = useStore((s) => s.defaultEdgeOptions); edge = defaultEdgeOptions ? { ...defaultEdgeOptions, ...edge } : edge; @@ -236,7 +236,7 @@ export function EdgeWrapper({ /> )} {isUpdatable && ( - edge={edge} isUpdatable={isUpdatable} edgeUpdaterRadius={edgeUpdaterRadius} diff --git a/packages/react/src/components/StoreUpdater/index.tsx b/packages/react/src/components/StoreUpdater/index.tsx index 6211ea59..9c77f5e0 100644 --- a/packages/react/src/components/StoreUpdater/index.tsx +++ b/packages/react/src/components/StoreUpdater/index.tsx @@ -68,7 +68,10 @@ const reactFlowFieldsToTrack = [ ] as const; type ReactFlowFieldsToTrack = (typeof reactFlowFieldsToTrack)[number]; -type StoreUpdaterProps = Pick, ReactFlowFieldsToTrack> & { +type StoreUpdaterProps = Pick< + ReactFlowProps, + ReactFlowFieldsToTrack +> & { rfId: string; }; @@ -86,7 +89,9 @@ const selector = (s: ReactFlowState) => ({ reset: s.reset, }); -export function StoreUpdater(props: StoreUpdaterProps) { +export function StoreUpdater( + props: StoreUpdaterProps +) { const { setNodes, setEdges, @@ -108,7 +113,7 @@ export function StoreUpdater(props: StoreUpdaterPr }; }, []); - const previousFields = useRef>>({ + const previousFields = useRef>>({ // these are values that are also passed directly to other components // than the StoreUpdater. We can reduce the number of setStore calls // by setting the same values here as prev fields. diff --git a/packages/react/src/container/EdgeRenderer/index.tsx b/packages/react/src/container/EdgeRenderer/index.tsx index 5fdaa1af..3112179e 100644 --- a/packages/react/src/container/EdgeRenderer/index.tsx +++ b/packages/react/src/container/EdgeRenderer/index.tsx @@ -6,10 +6,10 @@ import { useVisibleEdgeIds } from '../../hooks/useVisibleEdgeIds'; import MarkerDefinitions from './MarkerDefinitions'; import { GraphViewProps } from '../GraphView'; import { EdgeWrapper } from '../../components/EdgeWrapper'; -import type { ReactFlowState } from '../../types'; +import type { Edge, ReactFlowState, Node } from '../../types'; -type EdgeRendererProps = Pick< - GraphViewProps, +type EdgeRendererProps = Pick< + GraphViewProps, | 'onEdgeClick' | 'onEdgeDoubleClick' | 'defaultMarkerColor' @@ -40,7 +40,7 @@ const selector = (s: ReactFlowState) => ({ onError: s.onError, }); -function EdgeRendererComponent({ +function EdgeRendererComponent({ defaultMarkerColor, onlyRenderVisibleElements, rfId, @@ -56,7 +56,7 @@ function EdgeRendererComponent({ onEdgeDoubleClick, onEdgeUpdateStart, onEdgeUpdateEnd, -}: EdgeRendererProps) { +}: EdgeRendererProps) { const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector, shallow); const edgeIds = useVisibleEdgeIds(onlyRenderVisibleElements); @@ -66,7 +66,7 @@ function EdgeRendererComponent({ {edgeIds.map((id) => { return ( - key={id} id={id} edgesFocusable={edgesFocusable} @@ -95,4 +95,4 @@ function EdgeRendererComponent({ EdgeRendererComponent.displayName = 'EdgeRenderer'; -export const EdgeRenderer = memo(EdgeRendererComponent); +export const EdgeRenderer = memo(EdgeRendererComponent) as typeof EdgeRendererComponent; diff --git a/packages/react/src/container/FlowRenderer/index.tsx b/packages/react/src/container/FlowRenderer/index.tsx index 6aa49a2a..14216ac2 100644 --- a/packages/react/src/container/FlowRenderer/index.tsx +++ b/packages/react/src/container/FlowRenderer/index.tsx @@ -9,7 +9,7 @@ import { Pane } from '../Pane'; import { NodesSelection } from '../../components/NodesSelection'; import type { ReactFlowState, Node } from '../../types'; -export type FlowRendererProps = Omit< +export type FlowRendererProps = Omit< GraphViewProps, | 'snapToGrid' | 'nodeTypes' @@ -32,7 +32,7 @@ const selector = (s: ReactFlowState) => { return { nodesSelectionActive: s.nodesSelectionActive, userSelectionActive: s.userSelectionActive }; }; -function FlowRendererComponent({ +function FlowRendererComponent({ children, onPaneClick, onPaneMouseEnter, diff --git a/packages/react/src/container/GraphView/index.tsx b/packages/react/src/container/GraphView/index.tsx index 0615258c..f1965b04 100644 --- a/packages/react/src/container/GraphView/index.tsx +++ b/packages/react/src/container/GraphView/index.tsx @@ -8,15 +8,15 @@ import { useOnInitHandler } from '../../hooks/useOnInitHandler'; import { useViewportSync } from '../../hooks/useViewportSync'; import { ConnectionLineWrapper } from '../../components/ConnectionLine'; import { useNodeOrEdgeTypesWarning } from './useNodeOrEdgeTypesWarning'; -import type { Node, ReactFlowProps } from '../../types'; +import type { Edge, Node, ReactFlowProps } from '../../types'; -export type GraphViewProps = Omit< - ReactFlowProps, +export type GraphViewProps = Omit< + ReactFlowProps, 'onSelectionChange' | 'nodes' | 'edges' | 'onMove' | 'onMoveStart' | 'onMoveEnd' | 'elevateEdgesOnSelect' > & Required< Pick< - ReactFlowProps, + ReactFlowProps, | 'selectionKeyCode' | 'deleteKeyCode' | 'multiSelectionKeyCode' @@ -38,7 +38,7 @@ export type GraphViewProps = Omit< rfId: string; }; -function GraphViewComponent({ +function GraphViewComponent({ nodeTypes, edgeTypes, onInit, @@ -102,7 +102,7 @@ function GraphViewComponent({ rfId, viewport, onViewportChange, -}: GraphViewProps) { +}: GraphViewProps) { useNodeOrEdgeTypesWarning(nodeTypes); useNodeOrEdgeTypesWarning(edgeTypes); @@ -148,7 +148,7 @@ function GraphViewComponent({ isControlledViewport={!!viewport} > - edgeTypes={edgeTypes} onEdgeClick={onEdgeClick} onEdgeDoubleClick={onEdgeDoubleClick} diff --git a/packages/react/src/container/ReactFlow/index.tsx b/packages/react/src/container/ReactFlow/index.tsx index 2516b4e7..d4875419 100644 --- a/packages/react/src/container/ReactFlow/index.tsx +++ b/packages/react/src/container/ReactFlow/index.tsx @@ -17,7 +17,7 @@ import { StoreUpdater } from '../../components/StoreUpdater'; import { useColorModeClass } from '../../hooks/useColorModeClass'; import { GraphView } from '../GraphView'; import { Wrapper } from './Wrapper'; -import type { ReactFlowProps, ReactFlowRefType, Node } from '../../types'; +import type { ReactFlowProps, ReactFlowRefType, Node, Edge } from '../../types'; export const initNodeOrigin: NodeOrigin = [0, 0]; const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 }; @@ -30,13 +30,7 @@ const wrapperStyle: CSSProperties = { zIndex: 0, }; -declare module 'react' { - function forwardRef>( - render: (props: P, ref: React.Ref) => React.ReactNode | null - ): (props: P & React.RefAttributes) => React.ReactNode | null; -} - -function ReactFlow( +function ReactFlow( { nodes, edges, @@ -156,7 +150,7 @@ function ReactFlow( height, colorMode = 'light', ...rest - }: ReactFlowProps, + }: ReactFlowProps, ref: ForwardedRef ) { const rfId = id || '1'; @@ -172,7 +166,7 @@ function ReactFlow( id={id} > - + onInit={onInit} onNodeClick={onNodeClick} onEdgeClick={onEdgeClick} @@ -300,6 +294,4 @@ function ReactFlow( ); } -export default forwardRef(ReactFlow) as ( - props: ReactFlowProps & { ref?: React.ForwardedRef } -) => ReturnType; +export default forwardRef(ReactFlow) as typeof ReactFlow; diff --git a/packages/react/src/hooks/useEdges.ts b/packages/react/src/hooks/useEdges.ts index bd748abb..3ed9de39 100644 --- a/packages/react/src/hooks/useEdges.ts +++ b/packages/react/src/hooks/useEdges.ts @@ -11,8 +11,8 @@ const edgesSelector = (state: ReactFlowState) => state.edges; * @public * @returns An array of edges */ -export function useEdges(): Edge[] { - const edges = useStore(edgesSelector, shallow); +export function useEdges(): EdgeType[] { + const edges = useStore(edgesSelector, shallow) as EdgeType[]; return edges; } diff --git a/packages/react/src/hooks/useOnInitHandler.ts b/packages/react/src/hooks/useOnInitHandler.ts index 782b0379..5d43731b 100644 --- a/packages/react/src/hooks/useOnInitHandler.ts +++ b/packages/react/src/hooks/useOnInitHandler.ts @@ -1,15 +1,17 @@ import { useEffect, useRef } from 'react'; import { useReactFlow } from './useReactFlow'; -import type { OnInit, Node } from '../types'; +import type { OnInit, Node, Edge } from '../types'; /** * Hook for calling onInit handler. * * @internal */ -export function useOnInitHandler(onInit: OnInit | undefined) { - const rfInstance = useReactFlow(); +export function useOnInitHandler( + onInit: OnInit | undefined +) { + const rfInstance = useReactFlow(); const isInitialized = useRef(false); useEffect(() => { diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index bcb12cdf..a757ec23 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -53,7 +53,6 @@ export { type OnConnectStart, type OnConnect, type OnConnectEnd, - type IsValidConnection, type Viewport, type SnapGrid, PanOnScrollMode, diff --git a/packages/react/src/types/component-props.ts b/packages/react/src/types/component-props.ts index f5f854ab..ce6e94ad 100644 --- a/packages/react/src/types/component-props.ts +++ b/packages/react/src/types/component-props.ts @@ -18,7 +18,6 @@ import type { HandleType, SelectionMode, OnError, - IsValidConnection, ColorMode, SnapGrid, } from '@xyflow/system'; @@ -44,13 +43,15 @@ import type { EdgeMouseHandler, OnNodeDrag, OnBeforeDelete, + IsValidConnection, } from '.'; /** * ReactFlow component props. * @public */ -export interface ReactFlowProps extends Omit, 'onError'> { +export interface ReactFlowProps + extends Omit, 'onError'> { /** An array of nodes to render in a controlled flow. * @example * const nodes = [ @@ -73,11 +74,11 @@ export interface ReactFlowProps extends Omit extends Omit; /** This event handler is called when a user clicks on an edge */ - onEdgeClick?: (event: ReactMouseEvent, edge: Edge) => void; + onEdgeClick?: (event: ReactMouseEvent, edge: EdgeType) => void; /** This event handler is called when a user right clicks on an edge */ - onEdgeContextMenu?: EdgeMouseHandler; + onEdgeContextMenu?: EdgeMouseHandler; /** This event handler is called when mouse of a user enters an edge */ - onEdgeMouseEnter?: EdgeMouseHandler; + onEdgeMouseEnter?: EdgeMouseHandler; /** This event handler is called when mouse of a user moves over an edge */ - onEdgeMouseMove?: EdgeMouseHandler; + onEdgeMouseMove?: EdgeMouseHandler; /** This event handler is called when mouse of a user leaves an edge */ - onEdgeMouseLeave?: EdgeMouseHandler; + onEdgeMouseLeave?: EdgeMouseHandler; /** This event handler is called when a user double clicks on an edge */ - onEdgeDoubleClick?: EdgeMouseHandler; - onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge, handleType: HandleType) => void; - onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent, edge: Edge, handleType: HandleType) => void; - onEdgeUpdate?: OnEdgeUpdateFunc; + onEdgeDoubleClick?: EdgeMouseHandler; + onEdgeUpdateStart?: (event: ReactMouseEvent, edge: EdgeType, handleType: HandleType) => void; + onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent, edge: EdgeType, handleType: HandleType) => void; + onEdgeUpdate?: OnEdgeUpdateFunc; /** This event handler is called when a Node is updated * @example // Use NodesState hook to create edges and get onNodesChange handler * import ReactFlow, { useNodesState } from '@xyflow/react'; @@ -164,19 +165,19 @@ export interface ReactFlowProps extends Omit) */ - onEdgesChange?: OnEdgesChange; + onEdgesChange?: OnEdgesChange; /** This event handler gets called when a Node is deleted */ onNodesDelete?: OnNodesDelete; /** This event handler gets called when a Edge is deleted */ - onEdgesDelete?: OnEdgesDelete; + onEdgesDelete?: OnEdgesDelete; /** This event handler gets called when a Node or Edge is deleted */ - onDelete?: OnDelete; + onDelete?: OnDelete; /** This event handler gets called when a user starts to drag a selection box */ - onSelectionDragStart?: SelectionDragHandler; + onSelectionDragStart?: SelectionDragHandler; /** This event handler gets called when a user drags a selection box */ - onSelectionDrag?: SelectionDragHandler; + onSelectionDrag?: SelectionDragHandler; /** This event handler gets called when a user stops dragging a selection box */ - onSelectionDragStop?: SelectionDragHandler; + onSelectionDragStop?: SelectionDragHandler; onSelectionStart?: (event: ReactMouseEvent) => void; onSelectionEnd?: (event: ReactMouseEvent) => void; onSelectionContextMenu?: (event: ReactMouseEvent, nodes: NodeType[]) => void; @@ -201,7 +202,7 @@ export interface ReactFlowProps extends Omit; + onInit?: OnInit; /** This event handler is called while the user is either panning or zooming the viewport. */ onMove?: OnMove; /** This event handler gets called when a user starts to pan or zoom the viewport */ @@ -223,7 +224,7 @@ export interface ReactFlowProps extends Omit void; /** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */ - onBeforeDelete?: OnBeforeDelete; + onBeforeDelete?: OnBeforeDelete; /** Custom node types to be available in a flow. * * React Flow matches a node's type to a component in the nodeTypes object. diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index 2b514c96..4de6472e 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -30,7 +30,14 @@ export type EdgeLabelOptions = { export type EdgeUpdatable = boolean | HandleType; -export type DefaultEdge = EdgeBase & +/** + * The Edge type is mainly used for the `edges` that get passed to the ReactFlow component + * @public + */ +export type Edge< + EdgeData extends Record = Record, + EdgeType extends string | undefined = string | undefined +> = EdgeBase & EdgeLabelOptions & { style?: CSSProperties; className?: string; @@ -38,45 +45,41 @@ export type DefaultEdge = EdgeBase & focusable?: boolean; }; -type SmoothStepEdgeType = DefaultEdge & { +type SmoothStepEdgeType = Record> = Edge & { type: 'smoothstep'; pathOptions?: SmoothStepPathOptions; }; -type BezierEdgeType = DefaultEdge & { +type BezierEdgeType = Record> = Edge & { type: 'default'; pathOptions?: BezierPathOptions; }; -type StepEdgeType = DefaultEdge & { +type StepEdgeType = Record> = Edge & { type: 'step'; pathOptions?: StepPathOptions; }; -/** - * The Edge type is mainly used for the `edges` that get passed to the ReactFlow component - * @public - */ -export type Edge = DefaultEdge | SmoothStepEdgeType | BezierEdgeType | StepEdgeType; +export type BuiltInEdge = SmoothStepEdgeType | BezierEdgeType | StepEdgeType; -export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void; +export type EdgeMouseHandler = (event: ReactMouseEvent, edge: EdgeType) => void; -export type EdgeWrapperProps = { +export type EdgeWrapperProps = { id: string; edgesFocusable: boolean; edgesUpdatable: boolean; elementsSelectable: boolean; noPanClassName: string; - onClick?: EdgeMouseHandler; - onDoubleClick?: EdgeMouseHandler; - onEdgeUpdate?: OnEdgeUpdateFunc; - onContextMenu?: EdgeMouseHandler; - onMouseEnter?: EdgeMouseHandler; - onMouseMove?: EdgeMouseHandler; - onMouseLeave?: EdgeMouseHandler; + onClick?: EdgeMouseHandler; + onDoubleClick?: EdgeMouseHandler; + onEdgeUpdate?: OnEdgeUpdateFunc; + onContextMenu?: EdgeMouseHandler; + onMouseEnter?: EdgeMouseHandler; + onMouseMove?: EdgeMouseHandler; + onMouseLeave?: EdgeMouseHandler; edgeUpdaterRadius?: number; - onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge, handleType: HandleType) => void; - onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent, edge: Edge, handleType: HandleType) => void; + onEdgeUpdateStart?: (event: ReactMouseEvent, edge: EdgeType, handleType: HandleType) => void; + onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent, edge: EdgeType, handleType: HandleType) => void; rfId?: string; edgeTypes?: EdgeTypes; onError?: OnError; @@ -94,10 +97,10 @@ export type EdgeTextProps = HTMLAttributes & * Custom edge component props * @public */ -export type EdgeProps = Pick< - Edge, - 'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target' -> & +export type EdgeProps< + EdgeData extends Record = Record, + EdgeType extends string | undefined = string | undefined +> = Pick, 'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'> & EdgePosition & EdgeLabelOptions & { sourceHandleId?: string | null; @@ -185,7 +188,7 @@ export type StraightEdgeProps = Omit = (oldEdge: Edge, newConnection: Connection) => void; +export type OnEdgeUpdateFunc = (oldEdge: EdgeType, newConnection: Connection) => void; export type ConnectionLineComponentProps = { connectionLineStyle?: CSSProperties; diff --git a/packages/react/src/types/general.ts b/packages/react/src/types/general.ts index 4f3f127a..549921c2 100644 --- a/packages/react/src/types/general.ts +++ b/packages/react/src/types/general.ts @@ -12,6 +12,7 @@ import { XYPosition, NodeProps, OnBeforeDeleteBase, + Connection, } from '@xyflow/system'; import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps } from '.'; @@ -21,8 +22,11 @@ export type OnNodesChange = (changes: NodeChange = (changes: EdgeChange[]) => void; export type OnNodesDelete = (nodes: NodeType[]) => void; -export type OnEdgesDelete = (edges: Edge[]) => void; -export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void; +export type OnEdgesDelete = (edges: EdgeType[]) => void; +export type OnDelete = (params: { + nodes: NodeType[]; + edges: EdgeType[]; +}) => void; export type NodeTypes = { [key: string]: ComponentType }; export type EdgeTypes = { [key: string]: ComponentType }; @@ -139,3 +143,5 @@ export type OnBeforeDelete; + +export type IsValidConnection = (edge: EdgeType | Connection) => boolean; diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts index 9ef7b9c3..c3c0ea94 100644 --- a/packages/react/src/types/nodes.ts +++ b/packages/react/src/types/nodes.ts @@ -7,11 +7,10 @@ import { NodeTypes } from './general'; * The node data structure that gets used for the nodes prop. * @public */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type Node = NodeBase< - NodeData, - NodeType -> & { +export type Node< + NodeData extends Record = Record, + NodeType extends string | undefined = string | undefined +> = NodeBase & { style?: CSSProperties; className?: string; resizing?: boolean; diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index 38e3fbe9..30ac002a 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -21,7 +21,6 @@ import { type OnMoveStart, type OnMove, type OnMoveEnd, - type IsValidConnection, type UpdateConnection, type EdgeLookup, type ConnectionLookup, @@ -43,9 +42,10 @@ import type { OnDelete, OnNodeDrag, OnBeforeDelete, + IsValidConnection, } from '.'; -export type ReactFlowStore = { +export type ReactFlowStore = { rfId: string; width: number; height: number; @@ -53,10 +53,10 @@ export type ReactFlowStore = { nodes: NodeType[]; nodeLookup: NodeLookup; edges: Edge[]; - edgeLookup: EdgeLookup; + edgeLookup: EdgeLookup; connectionLookup: ConnectionLookup; onNodesChange: OnNodesChange | null; - onEdgesChange: OnEdgesChange | null; + onEdgesChange: OnEdgesChange | null; hasDefaultNodes: boolean; hasDefaultEdges: boolean; domNode: HTMLDivElement | null; @@ -124,8 +124,8 @@ export type ReactFlowStore = { fitViewDone: boolean; fitViewOnInitOptions: FitViewOptions | undefined; - onNodesDelete?: OnNodesDelete; - onEdgesDelete?: OnEdgesDelete; + onNodesDelete?: OnNodesDelete; + onEdgesDelete?: OnEdgesDelete; onDelete?: OnDelete; onError?: OnError; @@ -133,7 +133,7 @@ export type ReactFlowStore = { onViewportChangeStart?: OnViewportChange; onViewportChange?: OnViewportChange; onViewportChangeEnd?: OnViewportChange; - onBeforeDelete?: OnBeforeDelete; + onBeforeDelete?: OnBeforeDelete; onSelectionChangeHandlers: OnSelectionChangeFunc[]; @@ -142,15 +142,15 @@ export type ReactFlowStore = { autoPanOnNodeDrag: boolean; connectionRadius: number; - isValidConnection?: IsValidConnection; + isValidConnection?: IsValidConnection; lib: string; }; -export type ReactFlowActions = { +export type ReactFlowActions = { setNodes: (nodes: NodeType[]) => void; - setEdges: (edges: Edge[]) => void; - setDefaultNodesAndEdges: (nodes?: NodeType[], edges?: Edge[]) => void; + setEdges: (edges: EdgeType[]) => void; + setDefaultNodesAndEdges: (nodes?: NodeType[], edges?: EdgeType[]) => void; updateNodeDimensions: (updates: Map) => void; updateNodePositions: UpdateNodePositions; resetSelectedElements: () => void; @@ -169,4 +169,8 @@ export type ReactFlowActions = { fitView: (nodes: NodeType[], options?: FitViewOptions) => boolean; }; -export type ReactFlowState = ReactFlowStore & ReactFlowActions; +export type ReactFlowState = ReactFlowStore< + NodeType, + EdgeType +> & + ReactFlowActions; diff --git a/packages/svelte/src/lib/container/SvelteFlow/types.ts b/packages/svelte/src/lib/container/SvelteFlow/types.ts index aefa6f6e..b7dd2852 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/types.ts +++ b/packages/svelte/src/lib/container/SvelteFlow/types.ts @@ -10,7 +10,6 @@ import type { OnMoveEnd, CoordinateExtent, PanOnScrollMode, - IsValidConnection, OnError, ConnectionMode, PanelPosition, @@ -18,8 +17,7 @@ import type { ColorMode, OnConnect, OnConnectStart, - OnConnectEnd, - OnBeforeDelete + OnConnectEnd } from '@xyflow/system'; import type { @@ -31,7 +29,9 @@ import type { DefaultEdgeOptions, FitViewOptions, OnDelete, - OnEdgeCreate + OnEdgeCreate, + OnBeforeDelete, + IsValidConnection } from '$lib/types'; import type { Writable } from 'svelte/store'; diff --git a/packages/svelte/src/lib/hooks/useNodesData.ts b/packages/svelte/src/lib/hooks/useNodesData.ts index 8e24645c..d45c8c9e 100644 --- a/packages/svelte/src/lib/hooks/useNodesData.ts +++ b/packages/svelte/src/lib/hooks/useNodesData.ts @@ -3,7 +3,7 @@ import { derived, type Readable } from 'svelte/store'; import type { Node } from '$lib/types'; import { useStore } from '$lib/store'; -function areNodesDataEqual(a: Node['data'][] | null, b: Node['data'][] | null) { +function areNodesDataEqual(a: (Node['data'] | null)[] | null, b: (Node['data'] | null)[] | null) { if ((!a && !b) || (!a?.length && !b?.length)) { true; } diff --git a/packages/svelte/src/lib/index.ts b/packages/svelte/src/lib/index.ts index 76394872..c484ec7e 100644 --- a/packages/svelte/src/lib/index.ts +++ b/packages/svelte/src/lib/index.ts @@ -66,7 +66,6 @@ export { type OnConnectStart, type OnConnect, type OnConnectEnd, - type IsValidConnection, type Viewport, type SnapGrid, PanOnScrollMode, diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts index 6c18c877..40020aba 100644 --- a/packages/svelte/src/lib/store/initial-store.ts +++ b/packages/svelte/src/lib/store/initial-store.ts @@ -14,7 +14,6 @@ import { type MarkerProps, type PanZoomInstance, type CoordinateExtent, - type IsValidConnection, type NodeOrigin, type OnError, type Viewport, @@ -47,7 +46,8 @@ import type { FitViewOptions, OnDelete, OnEdgeCreate, - OnBeforeDelete + OnBeforeDelete, + IsValidConnection } from '$lib/types'; import { createNodesStore, createEdgesStore } from './utils'; import { initConnectionProps, type ConnectionProps } from './derived-connection-props'; diff --git a/packages/svelte/src/lib/types/edges.ts b/packages/svelte/src/lib/types/edges.ts index c99b201a..21bcdaa5 100644 --- a/packages/svelte/src/lib/types/edges.ts +++ b/packages/svelte/src/lib/types/edges.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import type { SvelteComponent, ComponentType } from 'svelte'; import type { EdgeBase, @@ -11,41 +10,49 @@ import type { import type { Node } from '$lib/types'; -export type DefaultEdge = EdgeBase & { +/** + * The Edge type is mainly used for the `edges` that get passed to the SvelteFlow component. + */ +export type Edge< + EdgeData extends Record = Record, + EdgeType extends string | undefined = string | undefined +> = EdgeBase & { label?: string; labelStyle?: string; style?: string; class?: string; }; -type SmoothStepEdgeType = DefaultEdge & { - type: 'smoothstep'; +type SmoothStepEdge = Record> = Edge< + EdgeData, + 'smoothstep' +> & { pathOptions?: SmoothStepPathOptions; }; -type BezierEdgeType = DefaultEdge & { - type: 'default'; +type BezierEdge = Record> = Edge< + EdgeData, + 'default' +> & { pathOptions?: BezierPathOptions; }; -type StepEdgeType = DefaultEdge & { - type: 'step'; +type StepEdge = Record> = Edge< + EdgeData, + 'step' +> & { pathOptions?: StepPathOptions; }; -/** - * The Edge type is mainly used for the `edges` that get passed to the SvelteFlow component. - */ -export type Edge = - | DefaultEdge - | SmoothStepEdgeType - | BezierEdgeType - | StepEdgeType; +export type BuiltInEdge = SmoothStepEdge | BezierEdge | StepEdge; /** * Custom edge component props. */ -export type EdgeProps = Omit, 'sourceHandle' | 'targetHandle' | 'type'> & +export type EdgeProps< + EdgeData extends Record = Record, + EdgeType extends string | undefined = string | undefined +> = Omit, 'sourceHandle' | 'targetHandle' | 'type'> & EdgePosition & { markerStart?: string; markerEnd?: string; diff --git a/packages/svelte/src/lib/types/general.ts b/packages/svelte/src/lib/types/general.ts index e67ad88c..fe728fae 100644 --- a/packages/svelte/src/lib/types/general.ts +++ b/packages/svelte/src/lib/types/general.ts @@ -57,3 +57,7 @@ export type OnBeforeDelete< NodeType extends Node = Node, EdgeType extends Edge = Edge > = OnBeforeDeleteBase; + +export type IsValidConnection = ( + edge: EdgeType | Connection +) => boolean; diff --git a/packages/svelte/src/lib/types/nodes.ts b/packages/svelte/src/lib/types/nodes.ts index 4121754a..7a0fe082 100644 --- a/packages/svelte/src/lib/types/nodes.ts +++ b/packages/svelte/src/lib/types/nodes.ts @@ -7,7 +7,7 @@ import type { NodeBase, NodeProps } from '@xyflow/system'; * @public */ export type Node< - NodeData = any, + NodeData extends Record = Record, NodeType extends string | undefined = string | undefined > = NodeBase & { class?: string; @@ -17,3 +17,5 @@ export type Node< export type NodeTypes = Record>>; export type DefaultNodeOptions = Partial>; + +export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>; diff --git a/packages/system/src/types/edges.ts b/packages/system/src/types/edges.ts index e28ad058..37c299e8 100644 --- a/packages/system/src/types/edges.ts +++ b/packages/system/src/types/edges.ts @@ -1,11 +1,14 @@ import { Position } from './utils'; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export type EdgeBase = { +export type EdgeBase< + EdgeData extends Record = Record, + EdgeType extends string | undefined = string | undefined +> = { /** Unique id of an edge */ id: string; /** Type of an edge defined in edgeTypes */ - type?: string; + type?: EdgeType; /** Id of source node */ source: string; /** Id of target node */ diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index b75e6d1b..4ac854ed 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -9,7 +9,10 @@ import { Optional } from '../utils/types'; * @typeParam T - type of the node data * @typeParam U - type of the node */ -export type NodeBase = { +export type NodeBase< + NodeData extends Record = Record, + NodeType extends string | undefined = string | undefined +> = { /** Unique id of a node */ id: string; /** Position of a node on the pane @@ -17,9 +20,9 @@ export type NodeBase */ position: XYPosition; /** Arbitrary data passed to a node */ - data: T; + data: NodeData; /** Type of node defined in nodeTypes */ - type?: U; + type?: NodeType; /** Only relevant for default, source, target nodeType. controls source position * @example 'right', 'left', 'top', 'bottom' */ @@ -70,7 +73,7 @@ export type NodeBase /** Holds a reference to the original node object provided by the user * (which may lack some fields, like `computed` or `[internalSymbol]`. Used * as an optimization to avoid certain operations. */ - userProvidedNode: NodeBase; + userProvidedNode: NodeBase; }; }; From a16c5c9bd5e6729b4aaa1c0a9e6ed4dd04bf9309 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 17 Feb 2024 16:32:59 +0100 Subject: [PATCH 06/10] chore(types): cleanup --- examples/react/src/examples/DragHandle/index.tsx | 2 +- packages/react/src/types/changes.ts | 1 - packages/react/src/types/edges.ts | 14 +++++++------- packages/react/src/types/general.ts | 1 - packages/react/src/types/instance.ts | 1 - packages/svelte/src/lib/types/nodes.ts | 1 - packages/system/src/types/edges.ts | 1 - 7 files changed, 8 insertions(+), 13 deletions(-) diff --git a/examples/react/src/examples/DragHandle/index.tsx b/examples/react/src/examples/DragHandle/index.tsx index b76d8ae7..a7a75fcf 100644 --- a/examples/react/src/examples/DragHandle/index.tsx +++ b/examples/react/src/examples/DragHandle/index.tsx @@ -14,7 +14,7 @@ const initialNodes: Node[] = [ dragHandle: '.custom-drag-handle', style: { border: '1px solid #ddd', padding: '20px 40px' }, position: { x: 200, y: 200 }, - data: null, + data: {}, }, ]; diff --git a/packages/react/src/types/changes.ts b/packages/react/src/types/changes.ts index 9a5e8fe5..adcb484d 100644 --- a/packages/react/src/types/changes.ts +++ b/packages/react/src/types/changes.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import type { XYPosition, Dimensions } from '@xyflow/system'; import type { Node, Edge } from '.'; diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index 4de6472e..dac682db 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -45,22 +45,22 @@ export type Edge< focusable?: boolean; }; -type SmoothStepEdgeType = Record> = Edge & { - type: 'smoothstep'; +type SmoothStepEdge = Record> = Edge< + EdgeData, + 'smoothstep' +> & { pathOptions?: SmoothStepPathOptions; }; -type BezierEdgeType = Record> = Edge & { - type: 'default'; +type BezierEdge = Record> = Edge & { pathOptions?: BezierPathOptions; }; -type StepEdgeType = Record> = Edge & { - type: 'step'; +type StepEdge = Record> = Edge & { pathOptions?: StepPathOptions; }; -export type BuiltInEdge = SmoothStepEdgeType | BezierEdgeType | StepEdgeType; +export type BuiltInEdge = SmoothStepEdge | BezierEdge | StepEdge; export type EdgeMouseHandler = (event: ReactMouseEvent, edge: EdgeType) => void; diff --git a/packages/react/src/types/general.ts b/packages/react/src/types/general.ts index 549921c2..0e1e1dc4 100644 --- a/packages/react/src/types/general.ts +++ b/packages/react/src/types/general.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { FitViewParamsBase, FitViewOptionsBase, diff --git a/packages/react/src/types/instance.ts b/packages/react/src/types/instance.ts index d34c23dc..1991580c 100644 --- a/packages/react/src/types/instance.ts +++ b/packages/react/src/types/instance.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-namespace */ import type { Rect, Viewport } from '@xyflow/system'; import type { Node, Edge, ViewportHelperFunctions } from '.'; diff --git a/packages/svelte/src/lib/types/nodes.ts b/packages/svelte/src/lib/types/nodes.ts index 7a0fe082..b3c572c7 100644 --- a/packages/svelte/src/lib/types/nodes.ts +++ b/packages/svelte/src/lib/types/nodes.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import type { ComponentType, SvelteComponent } from 'svelte'; import type { NodeBase, NodeProps } from '@xyflow/system'; diff --git a/packages/system/src/types/edges.ts b/packages/system/src/types/edges.ts index 37c299e8..39dc97bd 100644 --- a/packages/system/src/types/edges.ts +++ b/packages/system/src/types/edges.ts @@ -1,6 +1,5 @@ import { Position } from './utils'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any export type EdgeBase< EdgeData extends Record = Record, EdgeType extends string | undefined = string | undefined From 5e56dd58c01d61e955f3564e17e7d423dcbc51f9 Mon Sep 17 00:00:00 2001 From: moklick Date: Sun, 18 Feb 2024 19:54:55 +0100 Subject: [PATCH 07/10] chore(types): cleanup --- packages/svelte/src/lib/store/initial-store.ts | 2 +- packages/system/src/types/nodes.ts | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts index 40020aba..9c077ba0 100644 --- a/packages/svelte/src/lib/store/initial-store.ts +++ b/packages/svelte/src/lib/store/initial-store.ts @@ -79,7 +79,7 @@ export const getInitialStore = ({ height?: number; fitView?: boolean; }) => { - const nodeLookup = new Map(); + const nodeLookup: NodeLookup = new Map(); const nextNodes = adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin: [0, 0], elevateNodesOnSelect: false diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index 4ac854ed..aadd4a47 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { internalsSymbol } from '../constants'; import type { XYPosition, Position, CoordinateExtent, HandleElement } from '.'; import { Optional } from '../utils/types'; @@ -6,8 +5,8 @@ import { Optional } from '../utils/types'; /** * Framework independent node data structure. * - * @typeParam T - type of the node data - * @typeParam U - type of the node + * @typeParam NodeData - type of the node data + * @typeParam NodeType - type of the node */ export type NodeBase< NodeData extends Record = Record, @@ -77,17 +76,16 @@ export type NodeBase< }; }; -// props that get passed to a custom node /** * The node data structure that gets used for the nodes prop. * * @public * @param id - The id of the node. */ -export type NodeProps = { - /** Id of the node */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type NodeProps = { id: NodeBase['id']; - data: T; + data: NodeData; dragHandle: NodeBase['dragHandle']; type: NodeBase['type']; selected: NodeBase['selected']; From 07ddd97650283064cf7b706cfb94fdf68693c4fe Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 19 Feb 2024 10:45:48 +0100 Subject: [PATCH 08/10] refactor(react): add generics to useStoreApi --- .../src/additional-components/MiniMap/MiniMap.tsx | 4 ++-- .../react/src/components/NodesSelection/index.tsx | 5 ++--- packages/react/src/components/StoreUpdater/index.tsx | 2 +- packages/react/src/hooks/useStore.ts | 11 +++++++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/react/src/additional-components/MiniMap/MiniMap.tsx b/packages/react/src/additional-components/MiniMap/MiniMap.tsx index c4bbf13b..17a3f7f2 100644 --- a/packages/react/src/additional-components/MiniMap/MiniMap.tsx +++ b/packages/react/src/additional-components/MiniMap/MiniMap.tsx @@ -63,7 +63,7 @@ function MiniMapComponent({ zoomStep = 10, offsetScale = 5, }: MiniMapProps) { - const store = useStoreApi(); + const store = useStoreApi(); const svg = useRef(null); const { boundingRect, viewBB, rfId, panZoom, translateExtent, flowWidth, flowHeight } = useStore(selector, shallow); const elementWidth = (style?.width as number) ?? defaultWidth; @@ -120,7 +120,7 @@ function MiniMapComponent({ const onSvgNodeClick = onNodeClick ? useCallback((event: MouseEvent, nodeId: string) => { - const node = store.getState().nodeLookup.get(nodeId)! as NodeType; + const node = store.getState().nodeLookup.get(nodeId)!; onNodeClick(event, node); }, []) : undefined; diff --git a/packages/react/src/components/NodesSelection/index.tsx b/packages/react/src/components/NodesSelection/index.tsx index c0423827..6c037ba5 100644 --- a/packages/react/src/components/NodesSelection/index.tsx +++ b/packages/react/src/components/NodesSelection/index.tsx @@ -2,7 +2,6 @@ * The nodes selection rectangle gets displayed when a user * made a selection with on or several nodes */ - import { useRef, useEffect, type MouseEvent, type KeyboardEvent } from 'react'; import cc from 'classcat'; import { shallow } from 'zustand/shallow'; @@ -37,7 +36,7 @@ export function NodesSelection({ noPanClassName, disableKeyboardA11y, }: NodesSelectionProps) { - const store = useStoreApi(); + const store = useStoreApi(); const { width, height, transformString, userSelectionActive } = useStore(selector, shallow); const moveSelectedNodes = useMoveSelectedNodes(); @@ -62,7 +61,7 @@ export function NodesSelection({ const onContextMenu = onSelectionContextMenu ? (event: MouseEvent) => { const selectedNodes = store.getState().nodes.filter((n) => n.selected); - onSelectionContextMenu(event, selectedNodes as NodeType[]); + onSelectionContextMenu(event, selectedNodes); } : undefined; diff --git a/packages/react/src/components/StoreUpdater/index.tsx b/packages/react/src/components/StoreUpdater/index.tsx index 85159721..7917e3d6 100644 --- a/packages/react/src/components/StoreUpdater/index.tsx +++ b/packages/react/src/components/StoreUpdater/index.tsx @@ -115,7 +115,7 @@ export function StoreUpdater(); useEffect(() => { setDefaultNodesAndEdges(props.defaultNodes, props.defaultEdges); diff --git a/packages/react/src/hooks/useStore.ts b/packages/react/src/hooks/useStore.ts index 9a3a5e6b..8a600706 100644 --- a/packages/react/src/hooks/useStore.ts +++ b/packages/react/src/hooks/useStore.ts @@ -1,9 +1,10 @@ import { useContext, useMemo } from 'react'; -import { useStoreWithEqualityFn as useZustandStore } from 'zustand/traditional'; +import { UseBoundStoreWithEqualityFn, useStoreWithEqualityFn as useZustandStore } from 'zustand/traditional'; import { errorMessages } from '@xyflow/system'; import StoreContext from '../contexts/RFStoreContext'; -import type { ReactFlowState } from '../types'; +import type { Edge, Node, ReactFlowState } from '../types'; +import { StoreApi } from 'zustand'; const zustandErrorMessage = errorMessages['error001'](); @@ -32,8 +33,10 @@ function useStore( return useZustandStore(store, selector, equalityFn); } -function useStoreApi() { - const store = useContext(StoreContext); +function useStoreApi() { + const store = useContext(StoreContext) as UseBoundStoreWithEqualityFn< + StoreApi> + > | null; if (store === null) { throw new Error(zustandErrorMessage); From ea0e72aa0966878b800a7bcd2b1bd37f9bfb9c6d Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 19 Feb 2024 12:02:58 +0100 Subject: [PATCH 09/10] chore(svelte): export BuiltInNode type --- packages/svelte/src/lib/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/lib/index.ts b/packages/svelte/src/lib/index.ts index c484ec7e..20f0f284 100644 --- a/packages/svelte/src/lib/index.ts +++ b/packages/svelte/src/lib/index.ts @@ -45,7 +45,7 @@ export type { DefaultEdgeOptions } from '$lib/types/edges'; export type { HandleComponentProps, FitViewOptions } from '$lib/types/general'; -export type { Node, NodeTypes, DefaultNodeOptions } from '$lib/types/nodes'; +export type { Node, NodeTypes, DefaultNodeOptions, BuiltInNode } from '$lib/types/nodes'; export type { SvelteFlowStore } from '$lib/store/types'; // system types From 67fe7952f8966fe73192de5ae8225c96befbbfff Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 20 Feb 2024 00:04:20 +0100 Subject: [PATCH 10/10] chore(custom-nodes): use custom edge type --- examples/react/src/examples/CustomNode/index.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/react/src/examples/CustomNode/index.tsx b/examples/react/src/examples/CustomNode/index.tsx index 32bf34a0..16a99803 100644 --- a/examples/react/src/examples/CustomNode/index.tsx +++ b/examples/react/src/examples/CustomNode/index.tsx @@ -9,7 +9,6 @@ import { SnapGrid, useEdgesState, Background, - Edge, OnNodeDrag, OnInit, applyNodeChanges, @@ -17,6 +16,7 @@ import { OnConnect, OnBeforeDelete, BuiltInNode, + BuiltInEdge, } from '@xyflow/react'; import ColorSelectorNode from './ColorSelectorNode'; @@ -26,8 +26,9 @@ export type ColorSelectorNode = Node< 'selectorNode' >; export type MyNode = BuiltInNode | ColorSelectorNode; +export type MyEdge = BuiltInEdge; -const onInit: OnInit = (reactFlowInstance) => { +const onInit: OnInit = (reactFlowInstance) => { console.log('flow loaded:', reactFlowInstance); }; @@ -54,7 +55,7 @@ const CustomNodeFlow = () => { [setNodes] ); - const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); const [bgColor, setBgColor] = useState(initBgColor); @@ -144,7 +145,7 @@ const CustomNodeFlow = () => { [setEdges] ); - const onBeforeDelete: OnBeforeDelete = useCallback(async (params) => true, []); + const onBeforeDelete: OnBeforeDelete = useCallback(async (params) => true, []); return (