refactor(react/svelte): cleanup types

This commit is contained in:
moklick
2023-02-28 18:16:51 +01:00
parent dda21e1fd2
commit 63bb2045f4
96 changed files with 899 additions and 847 deletions
@@ -1,7 +1,7 @@
import { CSSProperties } from 'react'; import { CSSProperties } from 'react';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import type { ReactFlowState } from '../../types';
const style: CSSProperties = { display: 'none' }; const style: CSSProperties = { display: 'none' };
const ariaLiveStyle: CSSProperties = { const ariaLiveStyle: CSSProperties = {
@@ -6,16 +6,14 @@ import {
Position, Position,
ConnectionLineType, ConnectionLineType,
ConnectionMode, ConnectionMode,
type ConnectionLineComponent,
type ConnectionStatus, type ConnectionStatus,
type HandleType, type HandleType,
type ReactFlowState,
type ReactFlowStore,
} from '@reactflow/system'; } from '@reactflow/system';
import { getBezierPath, getSmoothStepPath } from '@reactflow/edge-utils'; import { getBezierPath, getSmoothStepPath } from '@reactflow/edge-utils';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge'; import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge';
import type { ConnectionLineComponent, ReactFlowState, ReactFlowStore } from '../../types';
type ConnectionLineProps = { type ConnectionLineProps = {
nodeId: string; nodeId: string;
@@ -1,8 +1,8 @@
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import type { ReactFlowState } from '../../types';
const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__edgelabel-renderer'); const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
@@ -1,5 +1,6 @@
import { isNumeric } from '@reactflow/utils'; import { isNumeric } from '@reactflow/utils';
import type { BaseEdgeProps } from '@reactflow/system';
import type { BaseEdgeProps } from '../../types';
import EdgeText from './EdgeText'; import EdgeText from './EdgeText';
@@ -1,8 +1,9 @@
import { memo } from 'react'; import { memo } from 'react';
import { Position, type BezierEdgeProps } from '@reactflow/system'; import { Position } from '@reactflow/system';
import { getBezierPath } from '@reactflow/edge-utils'; import { getBezierPath } from '@reactflow/edge-utils';
import BaseEdge from './BaseEdge'; import BaseEdge from './BaseEdge';
import type { BezierEdgeProps } from '../../types';
const BezierEdge = memo( const BezierEdge = memo(
({ ({
@@ -1,7 +1,8 @@
import { memo, useRef, useState, useEffect } from 'react'; import { memo, useRef, useState, useEffect, type FC, type PropsWithChildren } from 'react';
import type { FC, PropsWithChildren } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { EdgeTextProps, Rect } from '@reactflow/system'; import type { Rect } from '@reactflow/system';
import type { EdgeTextProps } from '../../types';
const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({ const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({
x, x,
@@ -1,8 +1,9 @@
import { memo } from 'react'; import { memo } from 'react';
import { Position, type EdgeProps } from '@reactflow/system'; import { Position } from '@reactflow/system';
import { getBezierEdgeCenter } from '@reactflow/edge-utils'; import { getBezierEdgeCenter } from '@reactflow/edge-utils';
import BaseEdge from './BaseEdge'; import BaseEdge from './BaseEdge';
import type { EdgeProps } from '../../types';
export interface GetSimpleBezierPathParams { export interface GetSimpleBezierPathParams {
sourceX: number; sourceX: number;
@@ -1,8 +1,9 @@
import { memo } from 'react'; import { memo } from 'react';
import { Position, type SmoothStepEdgeProps } from '@reactflow/system'; import { Position } from '@reactflow/system';
import { getSmoothStepPath } from '@reactflow/edge-utils'; import { getSmoothStepPath } from '@reactflow/edge-utils';
import BaseEdge from './BaseEdge'; import BaseEdge from './BaseEdge';
import type { SmoothStepEdgeProps } from '../../types';
const SmoothStepEdge = memo( const SmoothStepEdge = memo(
({ ({
@@ -1,7 +1,7 @@
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
import type { SmoothStepEdgeProps } from '@reactflow/system';
import SmoothStepEdge from './SmoothStepEdge'; import SmoothStepEdge from './SmoothStepEdge';
import type { SmoothStepEdgeProps } from '../../types';
const StepEdge = memo((props: SmoothStepEdgeProps) => ( const StepEdge = memo((props: SmoothStepEdgeProps) => (
<SmoothStepEdge <SmoothStepEdge
@@ -1,8 +1,8 @@
import { memo } from 'react'; import { memo } from 'react';
import type { EdgeProps } from '@reactflow/system';
import { getStraightPath } from '@reactflow/edge-utils'; import { getStraightPath } from '@reactflow/edge-utils';
import BaseEdge from './BaseEdge'; import BaseEdge from './BaseEdge';
import type { EdgeProps } from '../../types';
const StraightEdge = memo( const StraightEdge = memo(
({ ({
+2 -1
View File
@@ -1,6 +1,7 @@
import { MouseEvent as ReactMouseEvent } from 'react'; import { MouseEvent as ReactMouseEvent } from 'react';
import { StoreApi } from 'zustand'; import { StoreApi } from 'zustand';
import type { Edge, ReactFlowState } from '@reactflow/system';
import type { Edge, ReactFlowState } from '../../types';
export function getMouseHandler( export function getMouseHandler(
id: string, id: string,
@@ -1,14 +1,14 @@
import { memo, useState, useMemo, useRef } from 'react'; import { memo, useState, useMemo, useRef, type ComponentType, type KeyboardEvent } from 'react';
import type { ComponentType, KeyboardEvent } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { getMarkerId, elementSelectionKeys } from '@reactflow/utils'; import { getMarkerId, elementSelectionKeys } from '@reactflow/utils';
import type { EdgeProps, WrapEdgeProps, Connection } from '@reactflow/system'; import type { Connection } from '@reactflow/system';
import { useStoreApi } from '../../hooks/useStore'; import { useStoreApi } from '../../hooks/useStore';
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions'; import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
import { handlePointerDown } from '../Handle/handler'; import { handlePointerDown } from '../Handle/handler';
import { EdgeAnchor } from './EdgeAnchor'; import { EdgeAnchor } from './EdgeAnchor';
import { getMouseHandler } from './utils'; import { getMouseHandler } from './utils';
import type { EdgeProps, WrapEdgeProps } from '../../types';
export default (EdgeComponent: ComponentType<EdgeProps>) => { export default (EdgeComponent: ComponentType<EdgeProps>) => {
const EdgeWrapper = ({ const EdgeWrapper = ({
@@ -7,7 +7,7 @@ import {
pointToRendererPoint, pointToRendererPoint,
rendererPointToPoint, rendererPointToPoint,
} from '@reactflow/utils'; } from '@reactflow/utils';
import type { OnConnect, HandleType, ReactFlowState, Connection } from '@reactflow/system'; import type { OnConnect, HandleType, Connection } from '@reactflow/system';
import { import {
ConnectionHandle, ConnectionHandle,
@@ -19,6 +19,7 @@ import {
resetRecentHandle, resetRecentHandle,
ValidConnectionFunc, ValidConnectionFunc,
} from './utils'; } from './utils';
import type { ReactFlowState } from '../../types';
export function handlePointerDown({ export function handlePointerDown({
event, event,
@@ -1,13 +1,15 @@
import { memo, HTMLAttributes, forwardRef, MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react'; import { memo, HTMLAttributes, forwardRef, MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { errorMessages, Position, type HandleProps, type Connection, type ReactFlowState } from '@reactflow/system'; import { errorMessages, Position, type HandleProps, type Connection } from '@reactflow/system';
import { getHostForElement, isMouseEvent, addEdge } from '@reactflow/utils'; import { getHostForElement, isMouseEvent } from '@reactflow/utils';
import { useStore, useStoreApi } from '../../hooks/useStore'; import { useStore, useStoreApi } from '../../hooks/useStore';
import { useNodeId } from '../../contexts/NodeIdContext'; import { useNodeId } from '../../contexts/NodeIdContext';
import { handlePointerDown } from './handler'; import { handlePointerDown } from './handler';
import { isValidHandle } from './utils'; import { isValidHandle } from './utils';
import { addEdge } from '../../utils';
import type { ReactFlowState } from '../../types';
const alwaysValid = () => true; const alwaysValid = () => true;
+11 -2
View File
@@ -1,8 +1,17 @@
import { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react'; import { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
import { internalsSymbol, ConnectionMode, ConnectionStatus } from '@reactflow/system'; import {
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '@reactflow/system'; internalsSymbol,
ConnectionMode,
ConnectionStatus,
type Connection,
type HandleType,
type XYPosition,
type NodeHandleBounds,
} from '@reactflow/system';
import { getEventPosition } from '@reactflow/utils'; import { getEventPosition } from '@reactflow/utils';
import type { Node } from '../../types';
export type ConnectionHandle = { export type ConnectionHandle = {
id: string | null; id: string | null;
type: HandleType; type: HandleType;
+3 -1
View File
@@ -1,7 +1,9 @@
import { MouseEvent } from 'react'; import { MouseEvent } from 'react';
import { StoreApi } from 'zustand'; import { StoreApi } from 'zustand';
import { getDimensions } from '@reactflow/utils'; import { getDimensions } from '@reactflow/utils';
import { Position, type HandleElement, type Node, type NodeOrigin, type ReactFlowState } from '@reactflow/system'; import { Position, type HandleElement, type NodeOrigin } from '@reactflow/system';
import type { Node, ReactFlowState } from '../../types';
export const getHandleBounds = ( export const getHandleBounds = (
selector: string, selector: string,
@@ -1,8 +1,7 @@
import { useEffect, useRef, memo } from 'react'; import { useEffect, useRef, memo, type ComponentType, type MouseEvent, type KeyboardEvent } from 'react';
import type { ComponentType, MouseEvent, KeyboardEvent } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { elementSelectionKeys, isInputDOMNode } from '@reactflow/utils'; import { elementSelectionKeys, isInputDOMNode } from '@reactflow/utils';
import type { NodeProps, WrapNodeProps, XYPosition } from '@reactflow/system'; import type { NodeProps, XYPosition } from '@reactflow/system';
import { useStoreApi } from '../../hooks/useStore'; import { useStoreApi } from '../../hooks/useStore';
import { Provider } from '../../contexts/NodeIdContext'; import { Provider } from '../../contexts/NodeIdContext';
@@ -10,6 +9,7 @@ import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
import useDrag from '../../hooks/useDrag'; import useDrag from '../../hooks/useDrag';
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions'; import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
import { getMouseHandler, handleNodeClick } from './utils'; import { getMouseHandler, handleNodeClick } from './utils';
import type { WrapNodeProps } from '../../types';
export const arrowKeyDiffs: Record<string, XYPosition> = { export const arrowKeyDiffs: Record<string, XYPosition> = {
ArrowUp: { x: 0, y: -1 }, ArrowUp: { x: 0, y: -1 },
@@ -8,12 +8,12 @@ import type { MouseEvent, KeyboardEvent } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { getRectOfNodes } from '@reactflow/utils'; import { getRectOfNodes } from '@reactflow/utils';
import type { Node, ReactFlowState } from '@reactflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore'; import { useStore, useStoreApi } from '../../hooks/useStore';
import useDrag from '../../hooks/useDrag'; import useDrag from '../../hooks/useDrag';
import { arrowKeyDiffs } from '../Nodes/wrapNode'; import { arrowKeyDiffs } from '../Nodes/wrapNode';
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions'; import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
import type { Node, ReactFlowState } from '../../types';
export interface NodesSelectionProps { export interface NodesSelectionProps {
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void; onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
+2 -1
View File
@@ -1,8 +1,9 @@
import type { HTMLAttributes, ReactNode } from 'react'; import type { HTMLAttributes, ReactNode } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import type { PanelPosition, ReactFlowState } from '@reactflow/system'; import type { PanelPosition } from '@reactflow/system';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import type { ReactFlowState } from '../../types';
export type PanelProps = HTMLAttributes<HTMLDivElement> & { export type PanelProps = HTMLAttributes<HTMLDivElement> & {
position: PanelPosition; position: PanelPosition;
@@ -1,10 +1,9 @@
import { useRef } from 'react'; import { useRef, type FC, type PropsWithChildren } from 'react';
import type { FC, PropsWithChildren } from 'react';
import { StoreApi } from 'zustand'; import { StoreApi } from 'zustand';
import type { ReactFlowState } from '@reactflow/system';
import { Provider } from '../../contexts/RFStoreContext'; import { Provider } from '../../contexts/RFStoreContext';
import { createRFStore } from '../../store'; import { createRFStore } from '../../store';
import type { ReactFlowState } from '../../types';
const ReactFlowProvider: FC<PropsWithChildren<unknown>> = ({ children }) => { const ReactFlowProvider: FC<PropsWithChildren<unknown>> = ({ children }) => {
const storeRef = useRef<StoreApi<ReactFlowState> | null>(null); const storeRef = useRef<StoreApi<ReactFlowState> | null>(null);
@@ -1,8 +1,8 @@
import { memo, useEffect } from 'react'; import { memo, useEffect } from 'react';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import type { ReactFlowState, OnSelectionChangeFunc, Node, Edge } from '@reactflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore'; import { useStore, useStoreApi } from '../../hooks/useStore';
import type { ReactFlowState, OnSelectionChangeFunc, Node, Edge } from '../../types';
type SelectionListenerProps = { type SelectionListenerProps = {
onSelectionChange?: OnSelectionChangeFunc; onSelectionChange?: OnSelectionChangeFunc;
@@ -1,9 +1,10 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { StoreApi } from 'zustand'; import { StoreApi } from 'zustand';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import type { Node, Edge, ReactFlowState, CoordinateExtent, ReactFlowProps, ReactFlowStore } from '@reactflow/system'; import type { CoordinateExtent } from '@reactflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore'; import { useStore, useStoreApi } from '../../hooks/useStore';
import type { Node, Edge, ReactFlowState, ReactFlowProps, ReactFlowStore } from '../../types';
type StoreUpdaterProps = Pick< type StoreUpdaterProps = Pick<
ReactFlowProps, ReactFlowProps,
@@ -1,7 +1,7 @@
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import type { ReactFlowState } from '../../types';
const selector = (s: ReactFlowState) => ({ const selector = (s: ReactFlowState) => ({
userSelectionActive: s.userSelectionActive, userSelectionActive: s.userSelectionActive,
@@ -1,9 +1,10 @@
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import type { EdgeMarker, ReactFlowState } from '@reactflow/system'; import type { EdgeMarker } from '@reactflow/system';
import { getMarkerId } from '@reactflow/utils'; import { getMarkerId } from '@reactflow/utils';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import { useMarkerSymbol } from './MarkerSymbols'; import { useMarkerSymbol } from './MarkerSymbols';
import type { ReactFlowState } from '../../types';
type MarkerProps = EdgeMarker & { type MarkerProps = EdgeMarker & {
id: string; id: string;
@@ -1,14 +1,14 @@
import { memo, ReactNode } from 'react'; import { memo, ReactNode } from 'react';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import cc from 'classcat'; import cc from 'classcat';
import { errorMessages, ConnectionMode, Position, type Edge, type ReactFlowState } from '@reactflow/system'; import { errorMessages, ConnectionMode, Position } from '@reactflow/system';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import useVisibleEdges from '../../hooks/useVisibleEdges'; import useVisibleEdges from '../../hooks/useVisibleEdges';
import MarkerDefinitions from './MarkerDefinitions'; import MarkerDefinitions from './MarkerDefinitions';
import { getEdgePositions, getHandle, getNodeData } from './utils'; import { getEdgePositions, getHandle, getNodeData } from './utils';
import { GraphViewProps } from '../GraphView'; import { GraphViewProps } from '../GraphView';
import type { Edge, ReactFlowState } from '../../types';
type EdgeRendererProps = Pick< type EdgeRendererProps = Pick<
GraphViewProps, GraphViewProps,
@@ -2,12 +2,8 @@ import type { ComponentType } from 'react';
import { import {
internalsSymbol, internalsSymbol,
Position, Position,
type EdgeProps,
type EdgeTypes,
type EdgeTypesWrapped,
type HandleElement, type HandleElement,
type NodeHandleBounds, type NodeHandleBounds,
type Node,
type Rect, type Rect,
type Transform, type Transform,
type XYPosition, type XYPosition,
@@ -16,6 +12,7 @@ import { rectToBox } from '@reactflow/utils';
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges'; import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges';
import wrapEdge from '../../components/Edges/wrapEdge'; import wrapEdge from '../../components/Edges/wrapEdge';
import type { EdgeProps, EdgeTypes, EdgeTypesWrapped, Node } from '../../types';
export type CreateEdgeTypes = (edgeTypes: EdgeTypes) => EdgeTypesWrapped; export type CreateEdgeTypes = (edgeTypes: EdgeTypes) => EdgeTypesWrapped;
@@ -1,5 +1,4 @@
import { memo, type ReactNode } from 'react'; import { memo, type ReactNode } from 'react';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler'; import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
@@ -8,6 +7,7 @@ import { GraphViewProps } from '../GraphView';
import ZoomPane from '../ZoomPane'; import ZoomPane from '../ZoomPane';
import Pane from '../Pane'; import Pane from '../Pane';
import NodesSelection from '../../components/NodesSelection'; import NodesSelection from '../../components/NodesSelection';
import type { ReactFlowState } from '../../types';
export type FlowRendererProps = Omit< export type FlowRendererProps = Omit<
GraphViewProps, GraphViewProps,
@@ -1,5 +1,4 @@
import { memo } from 'react'; import { memo } from 'react';
import type { EdgeTypesWrapped, NodeTypesWrapped, ReactFlowProps } from '@reactflow/system';
import FlowRenderer from '../FlowRenderer'; import FlowRenderer from '../FlowRenderer';
import NodeRenderer from '../NodeRenderer'; import NodeRenderer from '../NodeRenderer';
@@ -7,6 +6,7 @@ import EdgeRenderer from '../EdgeRenderer';
import ViewportWrapper from '../Viewport'; import ViewportWrapper from '../Viewport';
import useOnInitHandler from '../../hooks/useOnInitHandler'; import useOnInitHandler from '../../hooks/useOnInitHandler';
import ConnectionLine from '../../components/ConnectionLine'; import ConnectionLine from '../../components/ConnectionLine';
import type { EdgeTypesWrapped, NodeTypesWrapped, ReactFlowProps } from '../../types';
export type GraphViewProps = Omit<ReactFlowProps, 'onSelectionChange' | 'nodes' | 'edges' | 'nodeTypes' | 'edgeTypes'> & export type GraphViewProps = Omit<ReactFlowProps, 'onSelectionChange' | 'nodes' | 'edges' | 'nodeTypes' | 'edgeTypes'> &
Required< Required<
@@ -1,7 +1,6 @@
import { memo, useMemo, useEffect, useRef } from 'react'; import { memo, useMemo, useEffect, useRef, type ComponentType } from 'react';
import type { ComponentType } from 'react';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { internalsSymbol, errorMessages, Position, type ReactFlowState, type WrapNodeProps } from '@reactflow/system'; import { internalsSymbol, errorMessages, Position } from '@reactflow/system';
import { clampPosition } from '@reactflow/utils'; import { clampPosition } from '@reactflow/utils';
import useVisibleNodes from '../../hooks/useVisibleNodes'; import useVisibleNodes from '../../hooks/useVisibleNodes';
@@ -9,6 +8,7 @@ import { useStore } from '../../hooks/useStore';
import { containerStyle } from '../../styles'; import { containerStyle } from '../../styles';
import { GraphViewProps } from '../GraphView'; import { GraphViewProps } from '../GraphView';
import { getPositionWithOrigin } from './utils'; import { getPositionWithOrigin } from './utils';
import type { ReactFlowState, WrapNodeProps } from '../../types';
type NodeRendererProps = Pick< type NodeRendererProps = Pick<
GraphViewProps, GraphViewProps,
@@ -1,11 +1,12 @@
import type { ComponentType } from 'react'; import type { ComponentType } from 'react';
import type { NodeTypes, NodeProps, NodeTypesWrapped, NodeOrigin, XYPosition } from '@reactflow/system'; import type { NodeProps, NodeOrigin, XYPosition } from '@reactflow/system';
import DefaultNode from '../../components/Nodes/DefaultNode'; import DefaultNode from '../../components/Nodes/DefaultNode';
import InputNode from '../../components/Nodes/InputNode'; import InputNode from '../../components/Nodes/InputNode';
import OutputNode from '../../components/Nodes/OutputNode'; import OutputNode from '../../components/Nodes/OutputNode';
import GroupNode from '../../components/Nodes/GroupNode'; import GroupNode from '../../components/Nodes/GroupNode';
import wrapNode from '../../components/Nodes/wrapNode'; import wrapNode from '../../components/Nodes/wrapNode';
import type { NodeTypes, NodeTypesWrapped } from '../../types';
export type CreateNodeTypes = (nodeTypes: NodeTypes) => NodeTypesWrapped; export type CreateNodeTypes = (nodeTypes: NodeTypes) => NodeTypesWrapped;
+8 -13
View File
@@ -2,22 +2,17 @@
* The user selection rectangle gets displayed when a user drags the mouse while pressing shift * The user selection rectangle gets displayed when a user drags the mouse while pressing shift
*/ */
import { memo, useRef, MouseEvent as ReactMouseEvent, ReactNode } from 'react'; import { memo, useRef, type MouseEvent as ReactMouseEvent, type ReactNode } from 'react';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import cc from 'classcat'; import cc from 'classcat';
import { getConnectedEdges, getNodesInside, getEventPosition } from '@reactflow/utils'; import { getNodesInside, getEventPosition } from '@reactflow/utils';
import { import { SelectionMode } from '@reactflow/system';
SelectionMode,
type ReactFlowProps,
type ReactFlowState,
type NodeChange,
type EdgeChange,
} from '@reactflow/system';
import UserSelection from '../../components/UserSelection'; import UserSelection from '../../components/UserSelection';
import { containerStyle } from '../../styles'; import { containerStyle } from '../../styles';
import { useStore, useStoreApi } from '../../hooks/useStore'; import { useStore, useStoreApi } from '../../hooks/useStore';
import { getSelectionChanges } from '../../utils/changes'; import { getSelectionChanges, getConnectedEdges } from '../../utils';
import type { ReactFlowProps, ReactFlowState, NodeChange, EdgeChange, Node } from '../../types';
type PaneProps = { type PaneProps = {
isSelecting: boolean; isSelecting: boolean;
@@ -135,7 +130,7 @@ const Pane = memo(
}; };
const onMouseMove = (event: ReactMouseEvent): void => { const onMouseMove = (event: ReactMouseEvent): void => {
const { userSelectionRect, nodeInternals, edges, transform, onNodesChange, onEdgesChange, nodeOrigin, getNodes } = const { userSelectionRect, edges, transform, onNodesChange, onEdgesChange, nodeOrigin, getNodes } =
store.getState(); store.getState();
if (!isSelecting || !containerBounds.current || !userSelectionRect) { if (!isSelecting || !containerBounds.current || !userSelectionRect) {
return; return;
@@ -156,8 +151,8 @@ const Pane = memo(
}; };
const nodes = getNodes(); const nodes = getNodes();
const selectedNodes = getNodesInside( const selectedNodes = getNodesInside<Node>(
nodeInternals, nodes,
nextUserSelectRect, nextUserSelectRect,
transform, transform,
selectionMode === SelectionMode.Partial, selectionMode === SelectionMode.Partial,
@@ -1,20 +1,14 @@
import { forwardRef } from 'react'; import { forwardRef, type CSSProperties } from 'react';
import type { CSSProperties } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { import {
ConnectionLineType, ConnectionLineType,
ConnectionMode, ConnectionMode,
PanOnScrollMode, PanOnScrollMode,
SelectionMode, SelectionMode,
type EdgeTypes,
type EdgeTypesWrapped,
type NodeOrigin, type NodeOrigin,
type NodeTypes,
type NodeTypesWrapped,
type ReactFlowProps,
type ReactFlowRefType,
type Viewport, type Viewport,
} from '@reactflow/system'; } from '@reactflow/system';
import { infiniteExtent } from '@reactflow/utils';
import Attribution from '../../components/Attribution'; import Attribution from '../../components/Attribution';
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges'; import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges';
@@ -29,8 +23,15 @@ import { createEdgeTypes } from '../EdgeRenderer/utils';
import { createNodeTypes } from '../NodeRenderer/utils'; import { createNodeTypes } from '../NodeRenderer/utils';
import GraphView from '../GraphView'; import GraphView from '../GraphView';
import Wrapper from './Wrapper'; import Wrapper from './Wrapper';
import { infiniteExtent } from '../../store/initialState';
import { useNodeOrEdgeTypes } from './utils'; import { useNodeOrEdgeTypes } from './utils';
import type {
EdgeTypes,
EdgeTypesWrapped,
NodeTypes,
NodeTypesWrapped,
ReactFlowProps,
ReactFlowRefType,
} from '../../types';
const defaultNodeTypes: NodeTypes = { const defaultNodeTypes: NodeTypes = {
input: InputNode, input: InputNode,
@@ -1,16 +1,11 @@
import { useMemo, useRef } from 'react'; import { useMemo, useRef } from 'react';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { import { errorMessages } from '@reactflow/system';
errorMessages,
type EdgeTypes,
type EdgeTypesWrapped,
type NodeTypes,
type NodeTypesWrapped,
} from '@reactflow/system';
import { devWarn } from '@reactflow/utils'; import { devWarn } from '@reactflow/utils';
import { CreateEdgeTypes } from '../EdgeRenderer/utils'; import { CreateEdgeTypes } from '../EdgeRenderer/utils';
import { CreateNodeTypes } from '../NodeRenderer/utils'; import { CreateNodeTypes } from '../NodeRenderer/utils';
import type { EdgeTypes, EdgeTypesWrapped, NodeTypes, NodeTypesWrapped } from '../../types';
export function useNodeOrEdgeTypes(nodeOrEdgeTypes: NodeTypes, createTypes: CreateNodeTypes): NodeTypesWrapped; export function useNodeOrEdgeTypes(nodeOrEdgeTypes: NodeTypes, createTypes: CreateNodeTypes): NodeTypesWrapped;
export function useNodeOrEdgeTypes(nodeOrEdgeTypes: EdgeTypes, createTypes: CreateEdgeTypes): EdgeTypesWrapped; export function useNodeOrEdgeTypes(nodeOrEdgeTypes: EdgeTypes, createTypes: CreateEdgeTypes): EdgeTypesWrapped;
@@ -1,7 +1,7 @@
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import type { ReactFlowState } from '../../types';
const selector = (s: ReactFlowState) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`; const selector = (s: ReactFlowState) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`;
@@ -1,17 +1,17 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { zoom, zoomIdentity } from 'd3-zoom'; import { zoom, zoomIdentity, type D3ZoomEvent } from 'd3-zoom';
import type { D3ZoomEvent } from 'd3-zoom';
import { select, pointer } from 'd3-selection'; import { select, pointer } from 'd3-selection';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { clamp } from '@reactflow/utils'; import { clamp } from '@reactflow/utils';
import { type Viewport, type ReactFlowState, CoordinateExtent, PanOnScrollMode } from '@reactflow/system'; import { CoordinateExtent, PanOnScrollMode, type Viewport } from '@reactflow/system';
import useKeyPress from '../../hooks/useKeyPress'; import useKeyPress from '../../hooks/useKeyPress';
import useResizeHandler from '../../hooks/useResizeHandler'; import useResizeHandler from '../../hooks/useResizeHandler';
import { useStore, useStoreApi } from '../../hooks/useStore'; import { useStore, useStoreApi } from '../../hooks/useStore';
import { containerStyle } from '../../styles'; import { containerStyle } from '../../styles';
import type { FlowRendererProps } from '../FlowRenderer'; import type { FlowRendererProps } from '../FlowRenderer';
import type { ReactFlowState } from '../../types';
type ZoomPaneProps = Omit< type ZoomPaneProps = Omit<
FlowRendererProps, FlowRendererProps,
+3 -3
View File
@@ -1,14 +1,14 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState, type RefObject, type MouseEvent } from 'react';
import type { RefObject, MouseEvent } from 'react';
import { drag } from 'd3-drag'; import { drag } from 'd3-drag';
import { select } from 'd3-selection'; import { select } from 'd3-selection';
import { calcAutoPan, getEventPosition } from '@reactflow/utils'; import { calcAutoPan, getEventPosition } from '@reactflow/utils';
import type { NodeDragItem, Node, SelectionDragHandler, UseDragEvent, XYPosition } from '@reactflow/system'; import type { NodeDragItem, UseDragEvent, XYPosition } from '@reactflow/system';
import { useStoreApi } from '../../hooks/useStore'; import { useStoreApi } from '../../hooks/useStore';
import { getDragItems, getEventHandlerParams, hasSelector, calcNextPosition } from './utils'; import { getDragItems, getEventHandlerParams, hasSelector, calcNextPosition } from './utils';
import { handleNodeClick } from '../../components/Nodes/utils'; import { handleNodeClick } from '../../components/Nodes/utils';
import useGetPointerPosition from '../useGetPointerPosition'; import useGetPointerPosition from '../useGetPointerPosition';
import type { Node, SelectionDragHandler } from '../../types';
export type UseDragData = { dx: number; dy: number }; export type UseDragData = { dx: number; dy: number };
+2 -2
View File
@@ -2,15 +2,15 @@ import type { RefObject } from 'react';
import { import {
errorMessages, errorMessages,
type CoordinateExtent, type CoordinateExtent,
type Node,
type NodeDragItem, type NodeDragItem,
type NodeInternals,
type NodeOrigin, type NodeOrigin,
type OnError, type OnError,
type XYPosition, type XYPosition,
} from '@reactflow/system'; } from '@reactflow/system';
import { clampPosition, isNumeric, getNodePositionWithOrigin } from '@reactflow/utils'; import { clampPosition, isNumeric, getNodePositionWithOrigin } from '@reactflow/utils';
import type { Node, NodeInternals } from '../../types';
export function isParentSelected(node: Node, nodeInternals: NodeInternals): boolean { export function isParentSelected(node: Node, nodeInternals: NodeInternals): boolean {
if (!node.parentNode) { if (!node.parentNode) {
return false; return false;
+1 -2
View File
@@ -1,6 +1,5 @@
import type { Edge, ReactFlowState } from '@reactflow/system';
import { useStore } from '../hooks/useStore'; import { useStore } from '../hooks/useStore';
import type { Edge, ReactFlowState } from '../types';
const edgesSelector = (state: ReactFlowState) => state.edges; const edgesSelector = (state: ReactFlowState) => state.edges;
+2 -1
View File
@@ -1,5 +1,6 @@
import { useStore } from '../hooks/useStore'; import { useStore } from '../hooks/useStore';
import type { Node, ReactFlowState } from '@reactflow/system';
import type { Node, ReactFlowState } from '../types';
const nodesSelector = (state: ReactFlowState) => state.getNodes(); const nodesSelector = (state: ReactFlowState) => state.getNodes();
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { useState, useCallback, type SetStateAction, type Dispatch } from 'react'; import { useState, useCallback, type SetStateAction, type Dispatch } from 'react';
import type { Node, NodeChange, Edge, EdgeChange } from '@reactflow/system';
import { applyNodeChanges, applyEdgeChanges } from '../utils/changes'; import { applyNodeChanges, applyEdgeChanges } from '../utils/changes';
import type { Node, NodeChange, Edge, EdgeChange } from '../types';
type ApplyChanges<ItemType, ChangesType> = (changes: ChangesType[], items: ItemType[]) => ItemType[]; type ApplyChanges<ItemType, ChangesType> = (changes: ChangesType[], items: ItemType[]) => ItemType[];
type OnChange<ChangesType> = (changes: ChangesType[]) => void; type OnChange<ChangesType> = (changes: ChangesType[]) => void;
@@ -1,6 +1,7 @@
import { internalsSymbol, type ReactFlowState } from '@reactflow/system'; import { internalsSymbol } from '@reactflow/system';
import { useStore } from './useStore'; import { useStore } from './useStore';
import type { ReactFlowState } from '../types';
const selector = (s: ReactFlowState) => { const selector = (s: ReactFlowState) => {
if (s.nodeInternals.size === 0) { if (s.nodeInternals.size === 0) {
+1 -1
View File
@@ -1,7 +1,7 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import type { OnInit } from '@reactflow/system';
import useReactFlow from './useReactFlow'; import useReactFlow from './useReactFlow';
import type { OnInit } from '../types';
function useOnInitHandler(onInit: OnInit | undefined) { function useOnInitHandler(onInit: OnInit | undefined) {
const rfInstance = useReactFlow(); const rfInstance = useReactFlow();
@@ -1,7 +1,7 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import type { OnSelectionChangeFunc } from '@reactflow/system';
import { useStoreApi } from './useStore'; import { useStoreApi } from './useStore';
import type { OnSelectionChangeFunc } from '../types';
export type UseOnSelectionChangeOptions = { export type UseOnSelectionChangeOptions = {
onChange?: OnSelectionChangeFunc; onChange?: OnSelectionChangeFunc;
+7 -6
View File
@@ -1,5 +1,10 @@
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { getConnectedEdges, getOverlappingArea, isRectObject, nodeToRect } from '@reactflow/utils'; import { getOverlappingArea, isRectObject, nodeToRect } from '@reactflow/utils';
import type { Rect } from '@reactflow/system';
import useViewportHelper from './useViewportHelper';
import { useStoreApi } from '../hooks/useStore';
import { getConnectedEdges } from '../utils';
import type { import type {
ReactFlowInstance, ReactFlowInstance,
Instance, Instance,
@@ -11,11 +16,7 @@ import type {
EdgeRemoveChange, EdgeRemoveChange,
NodeChange, NodeChange,
Node, Node,
Rect, } from '../types';
} from '@reactflow/system';
import useViewportHelper from './useViewportHelper';
import { useStoreApi } from '../hooks/useStore';
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> { export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
+2 -1
View File
@@ -1,8 +1,9 @@
import { useContext, useMemo } from 'react'; import { useContext, useMemo } from 'react';
import { useStore as useZustandStore, type StoreApi } from 'zustand'; import { useStore as useZustandStore, type StoreApi } from 'zustand';
import { errorMessages, type ReactFlowState } from '@reactflow/system'; import { errorMessages } from '@reactflow/system';
import StoreContext from '../contexts/RFStoreContext'; import StoreContext from '../contexts/RFStoreContext';
import type { ReactFlowState } from '../types';
const zustandErrorMessage = errorMessages['001'](); const zustandErrorMessage = errorMessages['001']();
+2 -1
View File
@@ -1,7 +1,8 @@
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import type { Viewport, ReactFlowState } from '@reactflow/system'; import type { Viewport } from '@reactflow/system';
import { useStore } from '../hooks/useStore'; import { useStore } from '../hooks/useStore';
import type { ReactFlowState } from '../types';
const viewportSelector = (state: ReactFlowState) => ({ const viewportSelector = (state: ReactFlowState) => ({
x: state.transform[0], x: state.transform[0],
+2 -1
View File
@@ -2,9 +2,10 @@ import { useMemo } from 'react';
import { zoomIdentity } from 'd3-zoom'; import { zoomIdentity } from 'd3-zoom';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { pointToRendererPoint, getTransformForBounds, getD3Transition, fitView } from '@reactflow/utils'; import { pointToRendererPoint, getTransformForBounds, getD3Transition, fitView } from '@reactflow/utils';
import type { ViewportHelperFunctions, ReactFlowState, XYPosition } from '@reactflow/system'; import type { XYPosition } from '@reactflow/system';
import { useStoreApi, useStore } from '../hooks/useStore'; import { useStoreApi, useStore } from '../hooks/useStore';
import type { ViewportHelperFunctions, ReactFlowState } from '../types';
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {}; const noop = () => {};
+2 -1
View File
@@ -1,9 +1,10 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { internalsSymbol, type ReactFlowState, type NodeInternals, type Edge } from '@reactflow/system'; import { internalsSymbol } from '@reactflow/system';
import { isNumeric } from '@reactflow/utils'; import { isNumeric } from '@reactflow/utils';
import { useStore } from '../hooks/useStore'; import { useStore } from '../hooks/useStore';
import { isEdgeVisible } from '../container/EdgeRenderer/utils'; import { isEdgeVisible } from '../container/EdgeRenderer/utils';
import { type ReactFlowState, type NodeInternals, type Edge } from '../types';
const defaultEdgeTree = [{ level: 0, isMaxLevel: true, edges: [] }]; const defaultEdgeTree = [{ level: 0, isMaxLevel: true, edges: [] }];
+2 -2
View File
@@ -1,15 +1,15 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { getNodesInside } from '@reactflow/utils'; import { getNodesInside } from '@reactflow/utils';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../hooks/useStore'; import { useStore } from '../hooks/useStore';
import type { Node, ReactFlowState } from '../types';
function useVisibleNodes(onlyRenderVisible: boolean) { function useVisibleNodes(onlyRenderVisible: boolean) {
const nodes = useStore( const nodes = useStore(
useCallback( useCallback(
(s: ReactFlowState) => (s: ReactFlowState) =>
onlyRenderVisible onlyRenderVisible
? getNodesInside(s.nodeInternals, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true) ? getNodesInside<Node>(s.getNodes(), { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true)
: s.getNodes(), : s.getNodes(),
[onlyRenderVisible] [onlyRenderVisible]
) )
+13 -18
View File
@@ -8,22 +8,6 @@ export { default as SimpleBezierEdge, getSimpleBezierPath } from './components/E
export { default as SmoothStepEdge } from './components/Edges/SmoothStepEdge'; export { default as SmoothStepEdge } from './components/Edges/SmoothStepEdge';
export { default as BaseEdge } from './components/Edges/BaseEdge'; export { default as BaseEdge } from './components/Edges/BaseEdge';
export {
isNode,
isEdge,
addEdge,
getOutgoers,
getIncomers,
getConnectedEdges,
updateEdge,
getTransformForBounds,
getRectOfNodes,
getNodePositionWithOrigin,
rectToBox,
boxToRect,
getBoundsOfRects,
} from '@reactflow/utils';
export { applyNodeChanges, applyEdgeChanges } from './utils/changes';
export { default as ReactFlowProvider } from './components/ReactFlowProvider'; export { default as ReactFlowProvider } from './components/ReactFlowProvider';
export { default as Panel } from './components/Panel'; export { default as Panel } from './components/Panel';
export { default as EdgeLabelRenderer } from './components/EdgeLabelRenderer'; export { default as EdgeLabelRenderer } from './components/EdgeLabelRenderer';
@@ -41,7 +25,18 @@ export { default as useOnSelectionChange } from './hooks/useOnSelectionChange';
export { default as useNodesInitialized } from './hooks/useNodesInitialized'; export { default as useNodesInitialized } from './hooks/useNodesInitialized';
export { default as useGetPointerPosition } from './hooks/useGetPointerPosition'; export { default as useGetPointerPosition } from './hooks/useGetPointerPosition';
export { useNodeId } from './contexts/NodeIdContext'; export { useNodeId } from './contexts/NodeIdContext';
export * from '@reactflow/system';
export * from '@reactflow/edge-utils'; export * from '@reactflow/edge-utils';
export * from '@reactflow/system';
export {
getTransformForBounds,
getRectOfNodes,
getNodePositionWithOrigin,
rectToBox,
boxToRect,
getBoundsOfRects,
} from '@reactflow/utils';
// export * from './types'; export { applyNodeChanges, applyEdgeChanges } from './utils/changes';
export { isNode, isEdge, getIncomers, getOutgoers, addEdge, updateEdge, getConnectedEdges } from './utils/general';
export * from './types';
+11 -9
View File
@@ -3,18 +3,9 @@ import { zoomIdentity } from 'd3-zoom';
import { clampPosition, getDimensions, fitView } from '@reactflow/utils'; import { clampPosition, getDimensions, fitView } from '@reactflow/utils';
import { import {
internalsSymbol, internalsSymbol,
type ReactFlowState,
type Node,
type Edge,
type NodeDimensionUpdate, type NodeDimensionUpdate,
type CoordinateExtent, type CoordinateExtent,
type NodeDimensionChange,
type EdgeSelectionChange,
type NodeSelectionChange,
type NodePositionChange,
type NodeDragItem, type NodeDragItem,
type UnselectNodesAndEdgesParams,
type NodeChange,
type XYPosition, type XYPosition,
} from '@reactflow/system'; } from '@reactflow/system';
@@ -22,6 +13,17 @@ import { applyNodeChanges, createSelectionChange, getSelectionChanges } from '..
import { getHandleBounds } from '../components/Nodes/utils'; import { getHandleBounds } from '../components/Nodes/utils';
import { createNodeInternals, updateAbsoluteNodePositions, updateNodesAndEdgesSelections } from './utils'; import { createNodeInternals, updateAbsoluteNodePositions, updateNodesAndEdgesSelections } from './utils';
import initialState from './initialState'; import initialState from './initialState';
import type {
ReactFlowState,
Node,
Edge,
NodeDimensionChange,
EdgeSelectionChange,
NodeSelectionChange,
NodePositionChange,
UnselectNodesAndEdgesParams,
NodeChange,
} from '../types';
const createRFStore = () => const createRFStore = () =>
createStore<ReactFlowState>((set, get) => ({ createStore<ReactFlowState>((set, get) => ({
+3 -6
View File
@@ -1,10 +1,7 @@
import { devWarn } from '@reactflow/utils'; import { devWarn, infiniteExtent } from '@reactflow/utils';
import { ConnectionMode, type CoordinateExtent, type ReactFlowStore } from '@reactflow/system'; import { ConnectionMode } from '@reactflow/system';
export const infiniteExtent: CoordinateExtent = [ import type { ReactFlowStore } from '../types';
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
];
const initialState: ReactFlowStore = { const initialState: ReactFlowStore = {
rfId: '1', rfId: '1',
+3 -11
View File
@@ -1,17 +1,9 @@
import type { StoreApi } from 'zustand'; import type { StoreApi } from 'zustand';
import { import { internalsSymbol, type XYZPosition, type NodeOrigin } from '@reactflow/system';
internalsSymbol,
type Edge,
type EdgeSelectionChange,
type Node,
type NodeInternals,
type NodeSelectionChange,
type ReactFlowState,
type XYZPosition,
type NodeOrigin,
} from '@reactflow/system';
import { isNumeric, getNodePositionWithOrigin } from '@reactflow/utils'; import { isNumeric, getNodePositionWithOrigin } from '@reactflow/utils';
import type { Edge, EdgeSelectionChange, Node, NodeInternals, NodeSelectionChange, ReactFlowState } from '../types';
type ParentNodes = Record<string, boolean>; type ParentNodes = Record<string, boolean>;
function calculateXYZPosition( function calculateXYZPosition(
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import type { XYPosition, Dimensions } from './utils'; import type { XYPosition, Dimensions } from '@reactflow/system';
import type { Node } from './nodes';
import type { Edge } from './edges'; import type { Node, Edge } from '.';
export type NodeDimensionChange = { export type NodeDimensionChange = {
id: string; id: string;
@@ -1,4 +1,22 @@
import type { CSSProperties, HTMLAttributes, MouseEvent as ReactMouseEvent, WheelEvent } from 'react'; import type { CSSProperties, HTMLAttributes, MouseEvent as ReactMouseEvent, WheelEvent } from 'react';
import type {
ConnectionMode,
ConnectionLineType,
OnConnect,
CoordinateExtent,
KeyCode,
PanOnScrollMode,
ProOptions,
PanelPosition,
OnMove,
OnMoveStart,
OnMoveEnd,
Viewport,
NodeOrigin,
HandleType,
SelectionMode,
OnError,
} from '@reactflow/system';
import type { import type {
OnSelectionChangeFunc, OnSelectionChangeFunc,
@@ -6,37 +24,21 @@ import type {
EdgeTypes, EdgeTypes,
Node, Node,
Edge, Edge,
ConnectionMode,
ConnectionLineType,
ConnectionLineComponent, ConnectionLineComponent,
OnConnectStart, OnConnectStart,
OnConnectEnd, OnConnectEnd,
OnConnect,
CoordinateExtent,
KeyCode,
PanOnScrollMode,
OnEdgeUpdateFunc, OnEdgeUpdateFunc,
OnInit, OnInit,
ProOptions,
PanelPosition,
DefaultEdgeOptions, DefaultEdgeOptions,
FitViewOptions, FitViewOptions,
OnNodesDelete, OnNodesDelete,
OnEdgesDelete, OnEdgesDelete,
OnNodesChange, OnNodesChange,
OnEdgesChange, OnEdgesChange,
OnMove,
OnMoveStart,
OnMoveEnd,
NodeDragHandler, NodeDragHandler,
NodeMouseHandler, NodeMouseHandler,
SelectionDragHandler, SelectionDragHandler,
Viewport,
NodeOrigin,
EdgeMouseHandler, EdgeMouseHandler,
HandleType,
SelectionMode,
OnError,
} from '.'; } from '.';
export type ReactFlowProps = HTMLAttributes<HTMLDivElement> & { export type ReactFlowProps = HTMLAttributes<HTMLDivElement> & {
+137
View File
@@ -0,0 +1,137 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { CSSProperties, HTMLAttributes, ReactNode, MouseEvent as ReactMouseEvent, ComponentType } from 'react';
import type {
BaseEdge,
BezierPathOptions,
Position,
SmoothStepPathOptions,
DefaultEdgeOptionsBase,
HandleType,
Connection,
ConnectionLineType,
HandleElement,
ConnectionStatus,
} from '@reactflow/system';
import { Node } from '.';
export type EdgeLabelOptions = {
label?: string | ReactNode;
labelStyle?: CSSProperties;
labelShowBg?: boolean;
labelBgStyle?: CSSProperties;
labelBgPadding?: [number, number];
labelBgBorderRadius?: number;
};
export type DefaultEdge<EdgeData = any> = BaseEdge<EdgeData> & {
style?: CSSProperties;
className?: string;
sourceNode?: Node;
targetNode?: Node;
} & EdgeLabelOptions;
type SmoothStepEdgeType<T> = DefaultEdge<T> & {
type: 'smoothstep';
pathOptions?: SmoothStepPathOptions;
};
type BezierEdgeType<T> = DefaultEdge<T> & {
type: 'default';
pathOptions?: BezierPathOptions;
};
export type Edge<T = any> = DefaultEdge<T> | SmoothStepEdgeType<T> | BezierEdgeType<T>;
export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void;
export type WrapEdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle'> & {
onClick?: EdgeMouseHandler;
onEdgeDoubleClick?: EdgeMouseHandler;
sourceHandleId?: string | null;
targetHandleId?: string | null;
sourceX: number;
sourceY: number;
targetX: number;
targetY: number;
sourcePosition: Position;
targetPosition: Position;
elementsSelectable?: boolean;
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;
rfId?: string;
isFocusable: boolean;
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
};
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
export type EdgeTextProps = HTMLAttributes<SVGElement> &
EdgeLabelOptions & {
x: number;
y: number;
};
// props that get passed to a custom edge
export type EdgeProps<T = any> = Pick<
Edge<T>,
'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'
> &
Pick<
WrapEdgeProps,
| 'sourceX'
| 'sourceY'
| 'targetX'
| 'targetY'
| 'sourcePosition'
| 'targetPosition'
| 'sourceHandleId'
| 'targetHandleId'
| 'interactionWidth'
> &
EdgeLabelOptions & {
markerStart?: string;
markerEnd?: string;
// @TODO: how can we get better types for pathOptions?
pathOptions?: any;
};
export type BaseEdgeProps = Pick<EdgeProps, 'style' | 'markerStart' | 'markerEnd' | 'interactionWidth'> &
EdgeLabelOptions & {
labelX?: number;
labelY?: number;
path: string;
};
export type SmoothStepEdgeProps<T = any> = EdgeProps<T> & {
pathOptions?: SmoothStepPathOptions;
};
export type BezierEdgeProps<T = any> = EdgeProps<T> & {
pathOptions?: BezierPathOptions;
};
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void;
export type ConnectionLineComponentProps = {
connectionLineStyle?: CSSProperties;
connectionLineType: ConnectionLineType;
fromNode?: Node;
fromHandle?: HandleElement;
fromX: number;
fromY: number;
toX: number;
toY: number;
fromPosition: Position;
toPosition: Position;
connectionStatus: ConnectionStatus | null;
};
export type ConnectionLineComponent = ComponentType<ConnectionLineComponentProps>;
+68
View File
@@ -0,0 +1,68 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type {
MouseEvent as ReactMouseEvent,
TouchEvent as ReactTouchEvent,
ComponentType,
MemoExoticComponent,
} from 'react';
import {
FitViewParamsBase,
FitViewOptionsBase,
NodeProps,
OnConnectStartParams,
ZoomInOut,
ZoomTo,
SetViewport,
GetZoom,
GetViewport,
SetCenter,
FitBounds,
Project,
} from '@reactflow/system';
import type { NodeChange, EdgeChange, Node, WrapNodeProps, Edge, EdgeProps, WrapEdgeProps, ReactFlowInstance } from '.';
export type OnNodesChange = (changes: NodeChange[]) => void;
export type OnEdgesChange = (changes: EdgeChange[]) => void;
export type OnNodesDelete = (nodes: Node[]) => void;
export type OnEdgesDelete = (edges: Edge[]) => void;
export type NodeTypes = { [key: string]: ComponentType<NodeProps> };
export type NodeTypesWrapped = { [key: string]: MemoExoticComponent<ComponentType<WrapNodeProps>> };
export type EdgeTypes = { [key: string]: ComponentType<EdgeProps> };
export type EdgeTypesWrapped = { [key: string]: MemoExoticComponent<ComponentType<WrapEdgeProps>> };
export type UnselectNodesAndEdgesParams = {
nodes?: Node[];
edges?: Edge[];
};
export type OnSelectionChangeParams = {
nodes: Node[];
edges: Edge[];
};
export type OnSelectionChangeFunc = (params: OnSelectionChangeParams) => void;
export type OnConnectStart = (event: ReactMouseEvent | ReactTouchEvent, params: OnConnectStartParams) => void;
export type OnConnectEnd = (event: MouseEvent | TouchEvent) => void;
export type FitViewParams = FitViewParamsBase<Node>;
export type FitViewOptions = FitViewOptionsBase<Node>;
export type FitView = (fitViewOptions?: FitViewOptions) => boolean;
export type OnInit<NodeData = any, EdgeData = any> = (reactFlowInstance: ReactFlowInstance<NodeData, EdgeData>) => void;
export type ViewportHelperFunctions = {
zoomIn: ZoomInOut;
zoomOut: ZoomInOut;
zoomTo: ZoomTo;
getZoom: GetZoom;
setViewport: SetViewport;
getViewport: GetViewport;
fitView: FitView;
setCenter: SetCenter;
fitBounds: FitBounds;
project: Project;
viewportInitialized: boolean;
};
+7
View File
@@ -0,0 +1,7 @@
export * from './nodes';
export * from './edges';
export * from './changes';
export * from './component-props';
export * from './general';
export * from './store';
export * from './instance';
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-namespace */
import { ViewportHelperFunctions, Viewport, Node, Edge, Rect } from '.'; import type { Rect, Viewport } from '@reactflow/system';
import type { Node, Edge, ViewportHelperFunctions } from '.';
export type ReactFlowJsonObject<NodeData = any, EdgeData = any> = { export type ReactFlowJsonObject<NodeData = any, EdgeData = any> = {
nodes: Node<NodeData>[]; nodes: Node<NodeData>[];
@@ -12,6 +13,7 @@ export type DeleteElementsOptions = {
nodes?: (Partial<Node> & { id: Node['id'] })[]; nodes?: (Partial<Node> & { id: Node['id'] })[];
edges?: (Partial<Edge> & { id: Edge['id'] })[]; edges?: (Partial<Edge> & { id: Edge['id'] })[];
}; };
export namespace Instance { export namespace Instance {
export type GetNodes<NodeData> = () => Node<NodeData>[]; export type GetNodes<NodeData> = () => Node<NodeData>[];
export type SetNodes<NodeData> = ( export type SetNodes<NodeData> = (
+46
View File
@@ -0,0 +1,46 @@
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
import type { BaseNode } from '@reactflow/system';
export type Node<NodeData = any, NodeType extends string | undefined = string | undefined> = BaseNode<
NodeData,
NodeType
> & {
style?: CSSProperties;
className?: string;
resizing?: boolean;
};
export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void;
export type NodeDragHandler = (event: ReactMouseEvent, node: Node, nodes: Node[]) => void;
export type SelectionDragHandler = (event: ReactMouseEvent, nodes: Node[]) => void;
export type WrapNodeProps<NodeData = any> = Pick<
Node<NodeData>,
'id' | 'data' | 'style' | 'className' | 'dragHandle' | 'sourcePosition' | 'targetPosition' | 'hidden' | 'ariaLabel'
> &
Required<Pick<Node<NodeData>, 'selected' | 'type' | 'zIndex'>> & {
isConnectable: boolean;
xPos: number;
yPos: number;
xPosOrigin: number;
yPosOrigin: number;
initialized: boolean;
isSelectable: boolean;
isDraggable: boolean;
isFocusable: boolean;
selectNodesOnDrag: boolean;
onClick?: NodeMouseHandler;
onDoubleClick?: NodeMouseHandler;
onMouseEnter?: NodeMouseHandler;
onMouseMove?: NodeMouseHandler;
onMouseLeave?: NodeMouseHandler;
onContextMenu?: NodeMouseHandler;
resizeObserver: ResizeObserver | null;
isParent: boolean;
noDragClassName: string;
noPanClassName: string;
rfId: string;
disableKeyboardA11y: boolean;
};
export type NodeInternals = Map<string, Node>;
+149
View File
@@ -0,0 +1,149 @@
import {
ConnectionMode,
ConnectionStatus,
CoordinateExtent,
D3SelectionInstance,
D3ZoomInstance,
HandleType,
NodeDimensionUpdate,
NodeDragItem,
NodeOrigin,
OnConnect,
OnError,
OnViewportChange,
SelectionRect,
SnapGrid,
StartHandle,
Transform,
XYPosition,
} from '@reactflow/system';
import type {
NodeDragHandler,
Edge,
Node,
NodeChange,
OnNodesChange,
OnEdgesChange,
NodeInternals,
OnConnectStart,
OnConnectEnd,
SelectionDragHandler,
DefaultEdgeOptions,
FitViewOptions,
OnNodesDelete,
OnEdgesDelete,
OnSelectionChangeFunc,
UnselectNodesAndEdgesParams,
} from '.';
export type ReactFlowStore = {
rfId: string;
width: number;
height: number;
transform: Transform;
nodeInternals: NodeInternals;
edges: Edge[];
onNodesChange: OnNodesChange | null;
onEdgesChange: OnEdgesChange | null;
hasDefaultNodes: boolean;
hasDefaultEdges: boolean;
domNode: HTMLDivElement | null;
paneDragging: boolean;
noPanClassName: string;
d3Zoom: D3ZoomInstance | null;
d3Selection: D3SelectionInstance | null;
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
minZoom: number;
maxZoom: number;
translateExtent: CoordinateExtent;
nodeExtent: CoordinateExtent;
nodeOrigin: NodeOrigin;
nodesSelectionActive: boolean;
userSelectionActive: boolean;
userSelectionRect: SelectionRect | null;
connectionNodeId: string | null;
connectionHandleId: string | null;
connectionHandleType: HandleType | null;
connectionPosition: XYPosition;
connectionStatus: ConnectionStatus | null;
connectionMode: ConnectionMode;
snapToGrid: boolean;
snapGrid: SnapGrid;
nodesDraggable: boolean;
nodesConnectable: boolean;
nodesFocusable: boolean;
edgesFocusable: boolean;
elementsSelectable: boolean;
elevateNodesOnSelect: boolean;
multiSelectionActive: boolean;
connectionStartHandle: StartHandle | null;
onNodeDragStart?: NodeDragHandler;
onNodeDrag?: NodeDragHandler;
onNodeDragStop?: NodeDragHandler;
onSelectionDragStart?: SelectionDragHandler;
onSelectionDrag?: SelectionDragHandler;
onSelectionDragStop?: SelectionDragHandler;
onConnect?: OnConnect;
onConnectStart?: OnConnectStart;
onConnectEnd?: OnConnectEnd;
onClickConnectStart?: OnConnectStart;
onClickConnectEnd?: OnConnectEnd;
connectOnClick: boolean;
defaultEdgeOptions?: DefaultEdgeOptions;
fitViewOnInit: boolean;
fitViewOnInitDone: boolean;
fitViewOnInitOptions: FitViewOptions | undefined;
onNodesDelete?: OnNodesDelete;
onEdgesDelete?: OnEdgesDelete;
onError?: OnError;
// event handlers
onViewportChangeStart?: OnViewportChange;
onViewportChange?: OnViewportChange;
onViewportChangeEnd?: OnViewportChange;
onSelectionChange?: OnSelectionChangeFunc;
ariaLiveMessage: string;
autoPanOnConnect: boolean;
autoPanOnNodeDrag: boolean;
connectionRadius: number;
};
export type ReactFlowActions = {
setNodes: (nodes: Node[]) => void;
getNodes: () => Node[];
setEdges: (edges: Edge[]) => void;
setDefaultNodesAndEdges: (nodes?: Node[], edges?: Edge[]) => void;
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
updateNodePositions: (nodeDragItems: NodeDragItem[] | Node[], positionChanged: boolean, dragging: boolean) => void;
resetSelectedElements: () => void;
unselectNodesAndEdges: (params?: UnselectNodesAndEdgesParams) => void;
addSelectedNodes: (nodeIds: string[]) => void;
addSelectedEdges: (edgeIds: string[]) => void;
setMinZoom: (minZoom: number) => void;
setMaxZoom: (maxZoom: number) => void;
setTranslateExtent: (translateExtent: CoordinateExtent) => void;
setNodeExtent: (nodeExtent: CoordinateExtent) => void;
cancelConnection: () => void;
reset: () => void;
triggerNodeChanges: (changes: NodeChange[]) => void;
panBy: (delta: XYPosition) => void;
};
export type ReactFlowState = ReactFlowStore & ReactFlowActions;
+1 -1
View File
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import type { Node, Edge, EdgeChange, NodeChange } from '@reactflow/system'; import type { Node, Edge, EdgeChange, NodeChange } from '../types';
function handleParentExpand(res: any[], updateItem: any) { function handleParentExpand(res: any[], updateItem: any) {
const parent = res.find((e) => e.id === updateItem.parentNode); const parent = res.find((e) => e.id === updateItem.parentNode);
+18
View File
@@ -0,0 +1,18 @@
import {
isNodeBase,
isEdgeBase,
addEdgeBase,
getOutgoersBase,
getIncomersBase,
updateEdgeBase,
getConnectedEdgesBase,
} from '@reactflow/utils';
import type { Edge, Node } from '../types';
export const isNode = isNodeBase<Node, Edge>;
export const isEdge = isEdgeBase<Node, Edge>;
export const getOutgoers = getOutgoersBase<Node, Edge>;
export const getIncomers = getIncomersBase<Node, Edge>;
export const addEdge = addEdgeBase<Edge>;
export const updateEdge = updateEdgeBase<Edge>;
export const getConnectedEdges = getConnectedEdgesBase<Node, Edge>;
+2
View File
@@ -0,0 +1,2 @@
export * from './changes';
export * from './general';
@@ -1,7 +1,7 @@
import { get, type Writable } from 'svelte/store'; import { get, type Writable } from 'svelte/store';
import { drag as d3Drag, type D3DragEvent, type SubjectPosition } from 'd3-drag'; import { drag as d3Drag, type D3DragEvent, type SubjectPosition } from 'd3-drag';
import { select } from 'd3-selection'; import { select } from 'd3-selection';
import type { XYPosition, CoordinateExtent, Transform, Node as RFNode } from '@reactflow/system'; import type { XYPosition, CoordinateExtent, Transform } from '@reactflow/system';
import { getDragItems, hasSelector, calcNextPosition } from './utils'; import { getDragItems, hasSelector, calcNextPosition } from './utils';
import type { Node } from '$lib/types'; import type { Node } from '$lib/types';
@@ -62,7 +62,7 @@ export default function drag(
dragItems = dragItems.map((n) => { dragItems = dragItems.map((n) => {
const nextPosition = { x: x - n.distance.x, y: y - n.distance.y }; const nextPosition = { x: x - n.distance.x, y: y - n.distance.y };
const updatedPos = calcNextPosition(n, nextPosition, get(nodes) as RFNode[]); const updatedPos = calcNextPosition(n, nextPosition, get(nodes));
// we want to make sure that we only fire a change event when there is a changes // we want to make sure that we only fire a change event when there is a changes
hasChange = hasChange =
@@ -89,7 +89,7 @@ export default function drag(
const pointerPos = getPointerPosition(event); const pointerPos = getPointerPosition(event);
console.log(pointerPos); console.log(pointerPos);
lastPos = pointerPos; lastPos = pointerPos;
dragItems = getDragItems(get(nodes) as RFNode[], pointerPos, nodeId); dragItems = getDragItems(get(nodes), pointerPos, nodeId);
}) })
.on('drag', (event: UseDragEvent) => { .on('drag', (event: UseDragEvent) => {
const pointerPos = getPointerPosition(event); const pointerPos = getPointerPosition(event);
+3 -37
View File
@@ -1,13 +1,7 @@
import type { import type { CoordinateExtent, NodeDragItem, NodeOrigin, XYPosition } from '@reactflow/system';
CoordinateExtent, import { clampPosition, isNumeric } from '@reactflow/utils';
Node,
NodeDragItem,
NodeInternals,
NodeOrigin,
XYPosition
} from '@reactflow/system';
import { clampPosition, isNumeric } from '../../../utils'; import type { Node } from '$lib/types';
export function isParentSelected(node: Node, nodes: Node[]): boolean { export function isParentSelected(node: Node, nodes: Node[]): boolean {
if (!node.parentNode) { if (!node.parentNode) {
@@ -122,31 +116,3 @@ export function calcNextPosition(
positionAbsolute positionAbsolute
}; };
} }
// returns two params:
// 1. the dragged node (or the first of the list, if we are dragging a node selection)
// 2. array of selected nodes (for multi selections)
export function getEventHandlerParams({
nodeId,
dragItems,
nodeInternals
}: {
nodeId?: string;
dragItems: NodeDragItem[];
nodeInternals: NodeInternals;
}): [Node, Node[]] {
const extentedDragItems: Node[] = dragItems.map((n) => {
const node = nodeInternals.get(n.id)!;
return {
...node,
position: n.position,
positionAbsolute: n.positionAbsolute
};
});
return [
nodeId ? extentedDragItems.find((n) => n.id === nodeId)! : extentedDragItems[0],
extentedDragItems
];
}
@@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import type { Writable } from 'svelte/store'; import type { Writable } from 'svelte/store';
import { select } from 'd3-selection'; import { select } from 'd3-selection';
import { zoom as d3Zoom, zoomIdentity } from 'd3-zoom'; import { zoom as d3Zoom, zoomIdentity, type D3ZoomEvent } from 'd3-zoom';
import type { D3ZoomEvent } from 'd3-zoom';
import type { D3SelectionInstance, D3ZoomInstance, Transform } from '@reactflow/system'; import type { D3SelectionInstance, D3ZoomInstance, Transform } from '@reactflow/system';
const isWrappedWithClass = (event: any, className: string | undefined) => const isWrappedWithClass = (event: any, className: string | undefined) =>
@@ -1,3 +1,4 @@
import { get, type Writable } from 'svelte/store';
import { import {
getHostForElement, getHostForElement,
calcAutoPan, calcAutoPan,
@@ -10,7 +11,6 @@ import type {
HandleType, HandleType,
Connection, Connection,
ConnectionMode, ConnectionMode,
Node,
XYPosition, XYPosition,
Transform Transform
} from '@reactflow/system'; } from '@reactflow/system';
@@ -25,8 +25,7 @@ import {
type ConnectionHandle, type ConnectionHandle,
type ValidConnectionFunc type ValidConnectionFunc
} from './utils'; } from './utils';
import { get, type Writable } from 'svelte/store'; import type { ConnectionData, Node } from '$lib/types';
import type { ConnectionData } from '$lib/types';
export function handlePointerDown({ export function handlePointerDown({
event, event,
@@ -9,9 +9,9 @@
type $$Props = HandleProps; type $$Props = HandleProps;
export let id: $$Props['id'] = undefined;
export let type: $$Props['type'] = 'source'; export let type: $$Props['type'] = 'source';
export let position: $$Props['position'] = Position.Top; export let position: $$Props['position'] = Position.Top;
export let id: $$Props['id'] = undefined;
export let isConnectable: $$Props['isConnectable'] = true; export let isConnectable: $$Props['isConnectable'] = true;
export let isValidConnection: $$Props['isValidConnection'] = (_: Connection) => true; export let isValidConnection: $$Props['isValidConnection'] = (_: Connection) => true;
let className: string | null = null; let className: string | null = null;
@@ -1,7 +1,9 @@
import { internalsSymbol, ConnectionMode, type ConnectionStatus } from '@reactflow/system'; import { internalsSymbol, ConnectionMode, type ConnectionStatus } from '@reactflow/system';
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '@reactflow/system'; import type { Connection, HandleType, XYPosition, NodeHandleBounds } from '@reactflow/system';
import { getEventPosition } from '@reactflow/utils'; import { getEventPosition } from '@reactflow/utils';
import type { Node } from '$lib/types';
export type ConnectionHandle = { export type ConnectionHandle = {
id: string | null; id: string | null;
type: HandleType; type: HandleType;
@@ -1,13 +1,13 @@
<script lang="ts"> <script lang="ts">
import type { BaseEdgeProps } from '$lib/types';
import EdgeLabelRenderer from '$lib/components/EdgeLabelRenderer/index.svelte'; import EdgeLabelRenderer from '$lib/components/EdgeLabelRenderer/index.svelte';
import type { BaseEdgeProps } from '$lib/types';
type $$Props = BaseEdgeProps; type $$Props = BaseEdgeProps;
export let path: $$Props['path']; export let path: $$Props['path'] = '';
export let label: $$Props['label']; export let label: $$Props['label'] = undefined;
export let labelX: $$Props['labelX']; export let labelX: $$Props['labelX'] = undefined;
export let labelY: $$Props['labelY']; export let labelY: $$Props['labelY'] = undefined;
export let interactionWidth: $$Props['interactionWidth'] = 20; export let interactionWidth: $$Props['interactionWidth'] = 20;
</script> </script>
@@ -4,9 +4,9 @@
import { useStore } from '$lib/store'; import { useStore } from '$lib/store';
import BezierEdge from '$lib/components/edges/StraightEdge.svelte'; import BezierEdge from '$lib/components/edges/StraightEdge.svelte';
import type { EdgeProps, WrapEdgeProps } from '$lib/types'; import type { EdgeProps, EdgeLayouted } from '$lib/types';
type $$Props = WrapEdgeProps; type $$Props = EdgeLayouted;
export let id: $$Props['id']; export let id: $$Props['id'];
export let type: $$Props['type'] = 'default'; export let type: $$Props['type'] = 'default';
@@ -16,6 +16,8 @@
export let sourceY: $$Props['sourceY'] = 0; export let sourceY: $$Props['sourceY'] = 0;
export let targetX: $$Props['targetX'] = 0; export let targetX: $$Props['targetX'] = 0;
export let targetY: $$Props['targetY'] = 0; export let targetY: $$Props['targetY'] = 0;
export let sourceHandleId: $$Props['sourceHandleId'] = undefined;
export let targetHandleId: $$Props['targetHandleId'] = undefined;
export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom; export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom;
export let targetPosition: $$Props['targetPosition'] = Position.Top; export let targetPosition: $$Props['targetPosition'] = Position.Top;
export let animated: $$Props['animated'] = false; export let animated: $$Props['animated'] = false;
@@ -30,8 +30,11 @@
<script lang="ts"> <script lang="ts">
import { useStore } from '$lib/store'; import { useStore } from '$lib/store';
import { SelectionMode, type Node, type Edge } from '@reactflow/system'; import { SelectionMode } from '@reactflow/system';
import { getConnectedEdges, getEventPosition, getNodesInside } from '@reactflow/utils'; import { getEventPosition, getNodesInside } from '@reactflow/utils';
import { getConnectedEdges} from '$lib/utils';
import type { Node, Edge } from '$lib/types';
const { const {
nodes, nodes,
@@ -107,8 +110,8 @@
height: Math.abs(mousePos.y - startY) height: Math.abs(mousePos.y - startY)
}; };
selectedNodes = getNodesInside( selectedNodes = getNodesInside<Node>(
new Map($nodes.map((node) => [node.id, node])), $nodes,
nextUserSelectRect, nextUserSelectRect,
$transform, $transform,
selectionMode === SelectionMode.Partial, selectionMode === SelectionMode.Partial,
@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { setContext, onMount } from 'svelte'; import { setContext, onMount } from 'svelte';
import { ConnectionLineType } from '@reactflow/system';
import cc from 'classcat'; import cc from 'classcat';
import { key, createStore } from '$lib/store'; import { key, createStore } from '$lib/store';
@@ -23,7 +24,7 @@
export let nodeTypes: $$Props['nodeTypes'] = undefined; export let nodeTypes: $$Props['nodeTypes'] = undefined;
export let selectionKey: $$Props['selectionKey'] = undefined; export let selectionKey: $$Props['selectionKey'] = undefined;
export let deleteKey: $$Props['deleteKey'] = undefined; export let deleteKey: $$Props['deleteKey'] = undefined;
export let connectionLineType: $$Props['connectionLineType'] = undefined; export let connectionLineType: $$Props['connectionLineType'] = ConnectionLineType.Bezier;
let className: $$Props['class'] = undefined; let className: $$Props['class'] = undefined;
export { className as class }; export { className as class };
@@ -51,27 +52,6 @@
}); });
}); });
// $: {
// const updatableProps = {
// defaultEdgeOptions,
// connectionMode,
// snapToGrid,
// snapGrid,
// nodesDraggable,
// connectOnClick,
// fitViewOnInit: fitView,
// fitViewOnInitOptions: fitViewOptions,
// };
// Object.keys(updatableProps).forEach((key) => {
// store.update((state) => ({
// ...state,
// [key]: valuesToUpdate[key],
// }));
// });
// }
$: { $: {
store.setNodes(nodes); store.setNodes(nodes);
} }
+2
View File
@@ -3,6 +3,8 @@ export { Controls, ControlButton } from '$lib/plugins/Controls';
export { Background, BackgroundVariant } from '$lib/plugins/Background'; export { Background, BackgroundVariant } from '$lib/plugins/Background';
export { Minimap } from '$lib/plugins/Minimap'; export { Minimap } from '$lib/plugins/Minimap';
export { default as Panel } from '$lib/container/Panel/index.svelte'; export { default as Panel } from '$lib/container/Panel/index.svelte';
export * from '$lib/types'; export * from '$lib/types';
export * from '$lib/utils';
export default SvelteFlow; export default SvelteFlow;
@@ -1,8 +1,8 @@
import { derived } from 'svelte/store';
import { getBezierPath, getSmoothStepPath, getStraightPath } from '@reactflow/edge-utils'; import { getBezierPath, getSmoothStepPath, getStraightPath } from '@reactflow/edge-utils';
import { ConnectionLineType, ConnectionMode, Position } from '@reactflow/system'; import { ConnectionLineType, ConnectionMode, Position, internalsSymbol } from '@reactflow/system';
import type { SvelteFlowStoreState } from './types'; import type { SvelteFlowStoreState } from './types';
import { derived } from 'svelte/store';
const oppositePosition = { const oppositePosition = {
[Position.Left]: Position.Right, [Position.Left]: Position.Right,
@@ -20,21 +20,21 @@ export function getConnectionPath(store: SvelteFlowStoreState) {
store.nodes, store.nodes,
store.transform store.transform
], ],
([$connection, $connectionLineType, $connectionMode, $nodes, $transform]) => { ([connection, connectionLineType, connectionMode, nodes, transform]) => {
if (!$connection.nodeId) { if (!connection.nodeId) {
return null; return null;
} }
const fromNode = $nodes.find((n) => n.id === $connection.nodeId); const fromNode = nodes.find((n) => n.id === connection.nodeId);
const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds; const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
const handleBoundsStrict = fromHandleBounds?.[$connection.handleType || 'source'] || []; const handleBoundsStrict = fromHandleBounds?.[connection.handleType || 'source'] || [];
const handleBoundsLoose = handleBoundsStrict const handleBoundsLoose = handleBoundsStrict
? handleBoundsStrict ? handleBoundsStrict
: fromHandleBounds?.[$connection.handleType === 'source' ? 'target' : 'source']!; : fromHandleBounds?.[connection.handleType === 'source' ? 'target' : 'source']!;
const handleBounds = const handleBounds =
$connectionMode === ConnectionMode.Strict ? handleBoundsStrict : handleBoundsLoose; connectionMode === ConnectionMode.Strict ? handleBoundsStrict : handleBoundsLoose;
const fromHandle = $connection.handleId const fromHandle = connection.handleId
? handleBounds.find((d) => d.id === $connection.handleId) ? handleBounds.find((d) => d.id === connection.handleId)
: handleBounds[0]; : handleBounds[0];
const fromHandleX = fromHandle const fromHandleX = fromHandle
? fromHandle.x + fromHandle.width / 2 ? fromHandle.x + fromHandle.width / 2
@@ -49,22 +49,22 @@ export function getConnectionPath(store: SvelteFlowStoreState) {
sourceX: fromX, sourceX: fromX,
sourceY: fromY, sourceY: fromY,
sourcePosition: fromPosition, sourcePosition: fromPosition,
targetX: (($connection.position?.x ?? 0) - $transform[0]) / $transform[2], targetX: ((connection.position?.x ?? 0) - transform[0]) / transform[2],
targetY: (($connection.position?.y ?? 0) - $transform[1]) / $transform[2], targetY: ((connection.position?.y ?? 0) - transform[1]) / transform[2],
targetPosition: toPosition targetPosition: toPosition
}; };
let path = ''; let path = '';
if ($connectionLineType === ConnectionLineType.Bezier) { if (connectionLineType === ConnectionLineType.Bezier) {
// we assume the destination position is opposite to the source position // we assume the destination position is opposite to the source position
[path] = getBezierPath(pathParams); [path] = getBezierPath(pathParams);
} else if ($connectionLineType === ConnectionLineType.Step) { } else if (connectionLineType === ConnectionLineType.Step) {
[path] = getSmoothStepPath({ [path] = getSmoothStepPath({
...pathParams, ...pathParams,
borderRadius: 0 borderRadius: 0
}); });
} else if ($connectionLineType === ConnectionLineType.SmoothStep) { } else if (connectionLineType === ConnectionLineType.SmoothStep) {
[path] = getSmoothStepPath(pathParams); [path] = getSmoothStepPath(pathParams);
} else { } else {
[path] = getStraightPath(pathParams); [path] = getStraightPath(pathParams);
@@ -2,7 +2,7 @@ import { derived } from 'svelte/store';
import { Position } from '@reactflow/system'; import { Position } from '@reactflow/system';
import { getEdgePositions, getHandle, getNodeData } from '$lib/container/EdgeRenderer/utils'; import { getEdgePositions, getHandle, getNodeData } from '$lib/container/EdgeRenderer/utils';
import type { WrapEdgeProps } from '$lib/types'; import type { EdgeLayouted } from '$lib/types';
import type { SvelteFlowStoreState } from './types'; import type { SvelteFlowStoreState } from './types';
export function getEdgesLayouted(store: SvelteFlowStoreState) { export function getEdgesLayouted(store: SvelteFlowStoreState) {
@@ -56,6 +56,6 @@ export function getEdgesLayouted(store: SvelteFlowStoreState) {
targetHandleId targetHandleId
}; };
}) })
.filter((e) => e !== null) as WrapEdgeProps[]; .filter((e) => e !== null) as EdgeLayouted[];
}); });
} }
+5 -13
View File
@@ -1,5 +1,6 @@
import { getContext } from 'svelte'; import { getContext } from 'svelte';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { zoomIdentity } from 'd3-zoom';
import { import {
type Transform, type Transform,
type NodeDragItem, type NodeDragItem,
@@ -7,23 +8,14 @@ import {
internalsSymbol, internalsSymbol,
type NodeOrigin, type NodeOrigin,
type ViewportHelperFunctionOptions, type ViewportHelperFunctionOptions,
type Node as RFNode,
type Connection, type Connection,
type XYPosition, type XYPosition,
type CoordinateExtent type CoordinateExtent
} from '@reactflow/system'; } from '@reactflow/system';
import { import { fitView as fitViewUtil, getD3Transition, getDimensions } from '@reactflow/utils';
fitView as fitViewUtil,
getConnectedEdges,
getD3Transition,
getDimensions,
addEdge as addEdgeUtil
} from '@reactflow/utils';
import { zoomIdentity } from 'd3-zoom';
import { getHandleBounds } from '../../utils'; import { getHandleBounds, getConnectedEdges, addEdge as addEdgeUtil } from '$lib/utils';
import type { EdgeTypes, NodeTypes, Node, Edge, ConnectionData } from '$lib/types'; import type { EdgeTypes, NodeTypes, Node, Edge, ConnectionData } from '$lib/types';
import { getEdgesLayouted } from './edges-layouted'; import { getEdgesLayouted } from './edges-layouted';
import { getConnectionPath } from './connection-path'; import { getConnectionPath } from './connection-path';
import { initConnectionData, initialStoreState } from './initial-store'; import { initConnectionData, initialStoreState } from './initial-store';
@@ -170,7 +162,7 @@ export function createStore({
return fitViewUtil( return fitViewUtil(
{ {
nodes: get(store.nodes) as RFNode[], nodes: get(store.nodes),
width: get(store.width), width: get(store.width),
height: get(store.height), height: get(store.height),
minZoom: 0.2, minZoom: 0.2,
@@ -227,7 +219,7 @@ export function createStore({
const initialHitEdges = deletableEdges.filter((e) => edgeIds.includes(e.id)); const initialHitEdges = deletableEdges.filter((e) => edgeIds.includes(e.id));
if (nodesToRemove || initialHitEdges) { if (nodesToRemove || initialHitEdges) {
const connectedEdges = getConnectedEdges(nodesToRemove as RFNode[], deletableEdges); const connectedEdges = getConnectedEdges(nodesToRemove, deletableEdges);
const edgesToRemove = [...initialHitEdges, ...connectedEdges]; const edgesToRemove = [...initialHitEdges, ...connectedEdges];
const edgeIdsToRemove = edgesToRemove.reduce<string[]>((res, edge) => { const edgeIdsToRemove = edgesToRemove.reduce<string[]>((res, edge) => {
if (!res.includes(edge.id)) { if (!res.includes(edge.id)) {
@@ -5,15 +5,15 @@ import {
type D3SelectionInstance, type D3SelectionInstance,
ConnectionMode, ConnectionMode,
ConnectionLineType, ConnectionLineType,
type SelectionRect,
type Transform, type Transform,
type NodeOrigin, type NodeOrigin
type Rect
} from '@reactflow/system'; } from '@reactflow/system';
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte'; import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
import InputNode from '$lib/components/nodes/InputNode.svelte'; import InputNode from '$lib/components/nodes/InputNode.svelte';
import OutputNode from '$lib/components/nodes/OutputNode.svelte'; import OutputNode from '$lib/components/nodes/OutputNode.svelte';
import type { Node, Edge, ConnectionData, NodeTypes, EdgeTypes } from '$lib/types'; import type { Node, Edge, ConnectionData, NodeTypes, EdgeTypes, EdgeLayouted } from '$lib/types';
import BezierEdge from '$lib/components/edges/BezierEdge.svelte'; import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
import StraightEdge from '$lib/components/edges/StraightEdge.svelte'; import StraightEdge from '$lib/components/edges/StraightEdge.svelte';
import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte'; import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte';
@@ -29,7 +29,7 @@ export const initConnectionData = {
export const initialStoreState = { export const initialStoreState = {
nodes: writable<Node[]>([]), nodes: writable<Node[]>([]),
edges: writable<Edge[]>([]), edges: writable<Edge[]>([]),
edgesLayouted: readable<Edge[]>([]), edgesLayouted: readable<EdgeLayouted[]>([]),
height: writable<number>(500), height: writable<number>(500),
width: writable<number>(500), width: writable<number>(500),
nodeOrigin: writable<NodeOrigin>([0.5, 0.5]), nodeOrigin: writable<NodeOrigin>([0.5, 0.5]),
@@ -39,7 +39,7 @@ export const initialStoreState = {
}), }),
id: writable<string | null>(null), id: writable<string | null>(null),
dragging: writable<boolean>(false), dragging: writable<boolean>(false),
selectionRect: writable<(Rect & { startX: number; startY: number }) | null>(null), selectionRect: writable<SelectionRect | null>(null),
selectionKeyPressed: writable<boolean>(false), selectionKeyPressed: writable<boolean>(false),
multiselectionKeyPressed: writable<boolean>(false), multiselectionKeyPressed: writable<boolean>(false),
deleteKeyPressed: writable<boolean>(false), deleteKeyPressed: writable<boolean>(false),
@@ -6,7 +6,7 @@ import type {
NodeDragItem NodeDragItem
} from '@reactflow/system'; } from '@reactflow/system';
import { initialStoreState } from './initial-store'; import type { initialStoreState } from './initial-store';
import type { Node, Edge, ConnectionData } from '$lib/types'; import type { Node, Edge, ConnectionData } from '$lib/types';
export type SvelteFlowStoreActions = { export type SvelteFlowStoreActions = {
+63 -1
View File
@@ -1,4 +1,66 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { SvelteComponentTyped } from 'svelte'; import type { SvelteComponentTyped } from 'svelte';
import type { EdgeProps } from '$lib/types'; import type {
BaseEdge,
BezierPathOptions,
Position,
SmoothStepPathOptions
} from '@reactflow/system';
import type { Node } from '$lib/types';
export type DefaultEdge<EdgeData = any> = BaseEdge<EdgeData> & {
label?: string;
style?: string;
class?: string;
sourceNode?: Node;
targetNode?: Node;
};
type SmoothStepEdgeType<T> = DefaultEdge<T> & {
type: 'smoothstep';
pathOptions?: SmoothStepPathOptions;
};
type BezierEdgeType<T> = DefaultEdge<T> & {
type: 'default';
pathOptions?: BezierPathOptions;
};
export type Edge<T = any> = DefaultEdge<T> | SmoothStepEdgeType<T> | BezierEdgeType<T>;
export type EdgeLayouted = Omit<Edge, 'sourceHandle' | 'targetHandle'> & {
sourceX: number;
sourceY: number;
targetX: number;
targetY: number;
sourcePosition: Position;
targetPosition: Position;
sourceHandleId?: string;
targetHandleId?: string;
};
export type EdgeProps = Pick<
EdgeLayouted,
| 'id'
| 'source'
| 'target'
| 'sourceX'
| 'sourceY'
| 'targetX'
| 'targetY'
| 'sourcePosition'
| 'targetPosition'
| 'animated'
| 'selected'
| 'label'
| 'interactionWidth'
>;
export type BaseEdgeProps = Pick<EdgeProps, 'interactionWidth' | 'label'> & {
path: string;
labelX?: number;
labelY?: number;
};
export type EdgeTypes = Record<string, typeof SvelteComponentTyped<EdgeProps>>; export type EdgeTypes = Record<string, typeof SvelteComponentTyped<EdgeProps>>;
+3 -2
View File
@@ -1,6 +1,7 @@
import type { Node, NodeTypes } from './nodes';
import type { ShortcutModifierDefinition } from '@svelte-put/shortcut'; import type { ShortcutModifierDefinition } from '@svelte-put/shortcut';
import type { ConnectionLineType, Edge, HandleType, XYPosition } from '@reactflow/system'; import type { ConnectionLineType, HandleType, XYPosition } from '@reactflow/system';
import type { Node, NodeTypes, Edge } from '.';
export type KeyModifier = ShortcutModifierDefinition; export type KeyModifier = ShortcutModifierDefinition;
export type KeyDefinitionObject = { key: string; modifier?: KeyModifier }; export type KeyDefinitionObject = { key: string; modifier?: KeyModifier };
-9
View File
@@ -1,12 +1,3 @@
export type {
Position,
XYPosition,
Edge,
BaseEdgeProps,
WrapEdgeProps,
EdgeProps
} from '@reactflow/system';
export * from './nodes'; export * from './nodes';
export * from './edges'; export * from './edges';
export * from './general'; export * from './general';
+7 -23
View File
@@ -1,33 +1,17 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { SvelteComponentTyped } from 'svelte'; import type { SvelteComponentTyped } from 'svelte';
import type { internalsSymbol, NodeHandleBounds, Position, XYPosition } from '@reactflow/system'; import type { BaseNode } from '@reactflow/system';
// @todo: currently the helper function only like Node from '@reactflow/core' // @todo: currently the helper function only like Node from '@reactflow/core'
// we need a base node type or helpes that accept Node like types // we need a base node type or helpes that accept Node like types
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Node<NodeData = any> = { export type Node<
id: string; NodeData = any,
type: string; NodeType extends string | undefined = string | undefined
data: NodeData; > = BaseNode<NodeData, NodeType> & {
position: XYPosition;
sourcePosition?: Position;
targetPosition?: Position;
positionAbsolute?: XYPosition;
width?: number;
height?: number;
selected?: boolean;
class?: string; class?: string;
style?: string; style?: string;
deletable?: boolean;
// not supported yet
parentNode?: string;
// only used internally
[internalsSymbol]?: {
z?: number;
handleBounds?: NodeHandleBounds;
isParent?: boolean;
};
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
+47
View File
@@ -0,0 +1,47 @@
import type { HandleElement, Position } from '@reactflow/system';
import {
isNodeBase,
isEdgeBase,
addEdgeBase,
getOutgoersBase,
getIncomersBase,
updateEdgeBase,
getConnectedEdgesBase,
getDimensions
} from '@reactflow/utils';
import type { Edge, Node } from '$lib/types';
export const isNode = isNodeBase<Node, Edge>;
export const isEdge = isEdgeBase<Node, Edge>;
export const getOutgoers = getOutgoersBase<Node, Edge>;
export const getIncomers = getIncomersBase<Node, Edge>;
export const addEdge = addEdgeBase<Edge>;
export const updateEdge = updateEdgeBase<Edge>;
export const getConnectedEdges = getConnectedEdgesBase<Node, Edge>;
export const getHandleBounds = (
selector: string,
nodeElement: HTMLDivElement,
zoom: number
): HandleElement[] | null => {
const handles = nodeElement.querySelectorAll(selector);
if (!handles || !handles.length) {
return null;
}
const handlesArray = Array.from(handles) as HTMLDivElement[];
const nodeBounds = nodeElement.getBoundingClientRect();
return handlesArray.map((handle): HandleElement => {
const handleBounds = handle.getBoundingClientRect();
return {
id: handle.getAttribute('data-handleid'),
position: handle.getAttribute('data-handlepos') as unknown as Position,
x: (handleBounds.left - nodeBounds.left) / zoom,
y: (handleBounds.top - nodeBounds.top) / zoom,
...getDimensions(handle)
};
});
};
-97
View File
@@ -1,97 +0,0 @@
import {
type CoordinateExtent,
type Dimensions,
type XYPosition,
type Node,
type XYZPosition,
type HandleElement,
internalsSymbol,
Position
} from '@reactflow/system';
export const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max);
export const clampPosition = (position: XYPosition = { x: 0, y: 0 }, extent: CoordinateExtent) => ({
x: clamp(position.x, extent[0][0], extent[1][0]),
y: clamp(position.y, extent[0][1], extent[1][1])
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isNumeric = (n: any): n is number => !isNaN(n) && isFinite(n);
export const getDimensions = (node: HTMLDivElement): Dimensions => ({
width: node.offsetWidth,
height: node.offsetHeight
});
type ParentNodes = Record<string, boolean>;
function calculateXYZPosition(node: Node, nodes: Node[], result: XYZPosition): XYZPosition {
if (!node.parentNode) {
return result;
}
const parentNode = nodes.find((n) => n.id === node.parentNode)!;
const parentNodePosition = parentNode.positionAbsolute!;
return calculateXYZPosition(parentNode, nodes, {
x: (result.x ?? 0) + parentNodePosition.x,
y: (result.y ?? 0) + parentNodePosition.y,
z:
(parentNode[internalsSymbol]?.z ?? 0) > (result.z ?? 0)
? parentNode[internalsSymbol]?.z ?? 0
: result.z ?? 0
});
}
export function updateAbsoluteNodePositions(nodes: Node[], parentNodes?: ParentNodes) {
nodes.forEach((node) => {
if (node.parentNode) {
throw new Error(`Parent node ${node.parentNode} not found`);
}
if (node.parentNode || parentNodes?.[node.id]) {
const { x, y, z } = calculateXYZPosition(node, nodes, {
...node.position,
z: node[internalsSymbol]?.z ?? 0
});
node.positionAbsolute = {
x,
y
};
node[internalsSymbol]!.z = z;
if (parentNodes?.[node.id]) {
node[internalsSymbol]!.isParent = true;
}
}
});
}
export const getHandleBounds = (
selector: string,
nodeElement: HTMLDivElement,
zoom: number
): HandleElement[] | null => {
const handles = nodeElement.querySelectorAll(selector);
if (!handles || !handles.length) {
return null;
}
const handlesArray = Array.from(handles) as HTMLDivElement[];
const nodeBounds = nodeElement.getBoundingClientRect();
return handlesArray.map((handle): HandleElement => {
const handleBounds = handle.getBoundingClientRect();
return {
id: handle.getAttribute('data-handleid'),
position: handle.getAttribute('data-handlepos') as unknown as Position,
x: (handleBounds.left - nodeBounds.left) / zoom,
y: (handleBounds.top - nodeBounds.top) / zoom,
...getDimensions(handle)
};
});
};
+3 -8
View File
@@ -4,11 +4,13 @@
"description": "Core system of React Flow.", "description": "Core system of React Flow.",
"keywords": [ "keywords": [
"react", "react",
"svelte",
"node-based UI", "node-based UI",
"graph", "graph",
"diagram", "diagram",
"workflow", "workflow",
"react-flow" "react-flow",
"svelte-flow"
], ],
"files": [ "files": [
"dist" "dist"
@@ -41,18 +43,11 @@
"@types/d3-selection": "^3.0.3", "@types/d3-selection": "^3.0.3",
"@types/d3-zoom": "^3.0.1" "@types/d3-zoom": "^3.0.1"
}, },
"peerDependencies": {
"react": ">=17",
"react-dom": ">=17"
},
"devDependencies": { "devDependencies": {
"@reactflow/eslint-config": "workspace:*", "@reactflow/eslint-config": "workspace:*",
"@reactflow/rollup-config": "workspace:*", "@reactflow/rollup-config": "workspace:*",
"@reactflow/tsconfig": "workspace:*", "@reactflow/tsconfig": "workspace:*",
"@types/node": "^18.7.16", "@types/node": "^18.7.16",
"@types/react": ">=17",
"@types/react-dom": ">=17",
"react": "^18.2.0",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },
"rollup": { "rollup": {
+2 -2
View File
@@ -1,4 +1,4 @@
import { Edge, HandleElement } from './types'; import { BaseEdge, HandleElement } from './types';
export const errorMessages = { export const errorMessages = {
'001': () => '001': () =>
@@ -11,7 +11,7 @@ export const errorMessages = {
'006': () => "Can't create edge. An edge needs a source and a target.", '006': () => "Can't create edge. An edge needs a source and a target.",
'007': (id: string) => `The old edge with id=${id} does not exist.`, '007': (id: string) => `The old edge with id=${id} does not exist.`,
'009': (type: string) => `Marker type "${type}" doesn't exist.`, '009': (type: string) => `Marker type "${type}" doesn't exist.`,
'008': (sourceHandle: HandleElement | null, edge: Edge) => '008': (sourceHandle: HandleElement | null, edge: BaseEdge) =>
`Couldn't create edge for ${!sourceHandle ? 'source' : 'target'} handle id: "${ `Couldn't create edge for ${!sourceHandle ? 'source' : 'target'} handle id: "${
!sourceHandle ? edge.sourceHandle : edge.targetHandle !sourceHandle ? edge.sourceHandle : edge.targetHandle
}", edge id: ${edge.id}.`, }", edge id: ${edge.id}.`,
+6 -127
View File
@@ -1,34 +1,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ // eslint-disable-next-line @typescript-eslint/no-explicit-any
import type { CSSProperties, ComponentType, HTMLAttributes, ReactNode, MouseEvent as ReactMouseEvent } from 'react'; export type BaseEdge<EdgeData = any> = {
import { ConnectionStatus, Position } from '.';
import type { Connection, HandleElement, HandleType, Node } from '.';
type EdgeLabelOptions = {
label?: string | ReactNode;
labelStyle?: CSSProperties;
labelShowBg?: boolean;
labelBgStyle?: CSSProperties;
labelBgPadding?: [number, number];
labelBgBorderRadius?: number;
};
// interface for the user edge items
type DefaultEdge<T = any> = {
id: string; id: string;
type?: string; type?: string;
source: string; source: string;
target: string; target: string;
sourceHandle?: string | null; sourceHandle?: string | null;
targetHandle?: string | null; targetHandle?: string | null;
style?: CSSProperties;
animated?: boolean; animated?: boolean;
hidden?: boolean; hidden?: boolean;
deletable?: boolean; deletable?: boolean;
data?: T; data?: EdgeData;
className?: string;
sourceNode?: Node;
targetNode?: Node;
selected?: boolean; selected?: boolean;
markerStart?: EdgeMarkerType; markerStart?: EdgeMarkerType;
markerEnd?: EdgeMarkerType; markerEnd?: EdgeMarkerType;
@@ -36,106 +17,22 @@ type DefaultEdge<T = any> = {
ariaLabel?: string; ariaLabel?: string;
interactionWidth?: number; interactionWidth?: number;
focusable?: boolean; focusable?: boolean;
} & EdgeLabelOptions; };
export type SmoothStepPathOptions = { export type SmoothStepPathOptions = {
offset?: number; offset?: number;
borderRadius?: number; borderRadius?: number;
}; };
type SmoothStepEdgeType<T> = DefaultEdge<T> & {
type: 'smoothstep';
pathOptions?: SmoothStepPathOptions;
};
export type BezierPathOptions = { export type BezierPathOptions = {
curvature?: number; curvature?: number;
}; };
type BezierEdgeType<T> = DefaultEdge<T> & { export type DefaultEdgeOptionsBase<EdgeType extends BaseEdge> = Omit<
type: 'default'; EdgeType,
pathOptions?: BezierPathOptions;
};
export type Edge<T = any> = DefaultEdge<T> | SmoothStepEdgeType<T> | BezierEdgeType<T>;
export type DefaultEdgeOptions = Omit<
Edge,
'id' | 'source' | 'target' | 'sourceHandle' | 'targetHandle' | 'sourceNode' | 'targetNode' 'id' | 'source' | 'target' | 'sourceHandle' | 'targetHandle' | 'sourceNode' | 'targetNode'
>; >;
export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void;
export type WrapEdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle'> & {
onClick?: EdgeMouseHandler;
onEdgeDoubleClick?: EdgeMouseHandler;
sourceHandleId?: string | null;
targetHandleId?: string | null;
sourceX: number;
sourceY: number;
targetX: number;
targetY: number;
sourcePosition: Position;
targetPosition: Position;
elementsSelectable?: boolean;
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;
rfId?: string;
isFocusable: boolean;
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
};
// props that get passed to a custom edge
export type EdgeProps<T = any> = Pick<
Edge<T>,
'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'
> &
Pick<
WrapEdgeProps,
| 'sourceX'
| 'sourceY'
| 'targetX'
| 'targetY'
| 'sourcePosition'
| 'targetPosition'
| 'sourceHandleId'
| 'targetHandleId'
| 'interactionWidth'
> &
EdgeLabelOptions & {
markerStart?: string;
markerEnd?: string;
// @TODO: how can we get better types for pathOptions?
pathOptions?: any;
};
export type BaseEdgeProps = Pick<EdgeProps, 'style' | 'markerStart' | 'markerEnd' | 'interactionWidth'> &
EdgeLabelOptions & {
labelX?: number;
labelY?: number;
path: string;
};
export type SmoothStepEdgeProps<T = any> = EdgeProps<T> & {
pathOptions?: SmoothStepPathOptions;
};
export type BezierEdgeProps<T = any> = EdgeProps<T> & {
pathOptions?: BezierPathOptions;
};
export type EdgeTextProps = HTMLAttributes<SVGElement> &
EdgeLabelOptions & {
x: number;
y: number;
};
export enum ConnectionLineType { export enum ConnectionLineType {
Bezier = 'default', Bezier = 'default',
Straight = 'straight', Straight = 'straight',
@@ -144,24 +41,6 @@ export enum ConnectionLineType {
SimpleBezier = 'simplebezier', SimpleBezier = 'simplebezier',
} }
export type ConnectionLineComponentProps = {
connectionLineStyle?: CSSProperties;
connectionLineType: ConnectionLineType;
fromNode?: Node;
fromHandle?: HandleElement;
fromX: number;
fromY: number;
toX: number;
toY: number;
fromPosition: Position;
toPosition: Position;
connectionStatus: ConnectionStatus | null;
};
export type ConnectionLineComponent = ComponentType<ConnectionLineComponentProps>;
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void;
export type EdgeMarker = { export type EdgeMarker = {
type: MarkerType; type: MarkerType;
color?: string; color?: string;
+7 -182
View File
@@ -1,45 +1,12 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import type {
MouseEvent as ReactMouseEvent,
TouchEvent as ReactTouchEvent,
ComponentType,
MemoExoticComponent,
} from 'react';
import type { D3DragEvent, Selection as D3Selection, SubjectPosition, ZoomBehavior } from 'd3'; import type { D3DragEvent, Selection as D3Selection, SubjectPosition, ZoomBehavior } from 'd3';
import type { XYPosition, Rect, Transform, CoordinateExtent } from './utils'; import type { XYPosition, Rect } from './utils';
import type { NodeChange, EdgeChange } from './changes'; import type { BaseNode, NodeOrigin } from './nodes';
import type { import type { HandleType } from './handles';
Node,
NodeInternals,
NodeDimensionUpdate,
NodeProps,
WrapNodeProps,
NodeDragItem,
NodeDragHandler,
SelectionDragHandler,
NodeOrigin,
} from './nodes';
import type { Edge, EdgeProps, WrapEdgeProps } from './edges';
import type { HandleType, StartHandle } from './handles';
import type { DefaultEdgeOptions } from '.';
import type { ReactFlowInstance } from './instance';
export type NodeTypes = { [key: string]: ComponentType<NodeProps> };
export type NodeTypesWrapped = { [key: string]: MemoExoticComponent<ComponentType<WrapNodeProps>> };
export type EdgeTypes = { [key: string]: ComponentType<EdgeProps> };
export type EdgeTypesWrapped = { [key: string]: MemoExoticComponent<ComponentType<WrapEdgeProps>> };
export type FitView = (fitViewOptions?: FitViewOptions) => boolean;
export type Project = (position: XYPosition) => XYPosition; export type Project = (position: XYPosition) => XYPosition;
export type OnNodesChange = (changes: NodeChange[]) => void;
export type OnEdgesChange = (changes: EdgeChange[]) => void;
export type OnNodesDelete = (nodes: Node[]) => void;
export type OnEdgesDelete = (edges: Edge[]) => void;
export type OnMove = (event: MouseEvent | TouchEvent, viewport: Viewport) => void; export type OnMove = (event: MouseEvent | TouchEvent, viewport: Viewport) => void;
export type OnMoveStart = OnMove; export type OnMoveStart = OnMove;
export type OnMoveEnd = OnMove; export type OnMoveEnd = OnMove;
@@ -52,8 +19,6 @@ export type SetViewport = (viewport: Viewport, options?: ViewportHelperFunctionO
export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => void; export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => void;
export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => void; export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => void;
export type OnInit<NodeData = any, EdgeData = any> = (reactFlowInstance: ReactFlowInstance<NodeData, EdgeData>) => void;
export interface Connection { export interface Connection {
source: string | null; source: string | null;
target: string | null; target: string | null;
@@ -70,8 +35,8 @@ export enum ConnectionMode {
export type OnConnect = (connection: Connection) => void; export type OnConnect = (connection: Connection) => void;
export type FitViewParams = { export type FitViewParamsBase<NodeType extends BaseNode> = {
nodes: Node[]; nodes: NodeType[];
width: number; width: number;
height: number; height: number;
nodeOrigin: NodeOrigin; nodeOrigin: NodeOrigin;
@@ -81,13 +46,13 @@ export type FitViewParams = {
maxZoom: number; maxZoom: number;
}; };
export type FitViewOptions = { export type FitViewOptionsBase<NodeType extends BaseNode> = {
padding?: number; padding?: number;
includeHiddenNodes?: boolean; includeHiddenNodes?: boolean;
minZoom?: number; minZoom?: number;
maxZoom?: number; maxZoom?: number;
duration?: number; duration?: number;
nodes?: (Partial<Node> & { id: Node['id'] })[]; nodes?: (Partial<NodeType> & { id: NodeType['id'] })[];
}; };
export type OnConnectStartParams = { export type OnConnectStartParams = {
@@ -96,9 +61,6 @@ export type OnConnectStartParams = {
handleType: HandleType | null; handleType: HandleType | null;
}; };
export type OnConnectStart = (event: ReactMouseEvent | ReactTouchEvent, params: OnConnectStartParams) => void;
export type OnConnectEnd = (event: MouseEvent | TouchEvent) => void;
export type Viewport = { export type Viewport = {
x: number; x: number;
y: number; y: number;
@@ -127,150 +89,13 @@ export type FitBoundsOptions = ViewportHelperFunctionOptions & {
padding?: number; padding?: number;
}; };
export type UnselectNodesAndEdgesParams = {
nodes?: Node[];
edges?: Edge[];
};
export type OnViewportChange = (viewport: Viewport) => void; export type OnViewportChange = (viewport: Viewport) => void;
export type ViewportHelperFunctions = {
zoomIn: ZoomInOut;
zoomOut: ZoomInOut;
zoomTo: ZoomTo;
getZoom: GetZoom;
setViewport: SetViewport;
getViewport: GetViewport;
fitView: FitView;
setCenter: SetCenter;
fitBounds: FitBounds;
project: Project;
viewportInitialized: boolean;
};
export type D3ZoomInstance = ZoomBehavior<Element, unknown>; export type D3ZoomInstance = ZoomBehavior<Element, unknown>;
export type D3SelectionInstance = D3Selection<Element, unknown, null, undefined>; export type D3SelectionInstance = D3Selection<Element, unknown, null, undefined>;
export type ReactFlowStore = {
rfId: string;
width: number;
height: number;
transform: Transform;
nodeInternals: NodeInternals;
edges: Edge[];
onNodesChange: OnNodesChange | null;
onEdgesChange: OnEdgesChange | null;
hasDefaultNodes: boolean;
hasDefaultEdges: boolean;
domNode: HTMLDivElement | null;
paneDragging: boolean;
noPanClassName: string;
d3Zoom: D3ZoomInstance | null;
d3Selection: D3SelectionInstance | null;
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
minZoom: number;
maxZoom: number;
translateExtent: CoordinateExtent;
nodeExtent: CoordinateExtent;
nodeOrigin: NodeOrigin;
nodesSelectionActive: boolean;
userSelectionActive: boolean;
userSelectionRect: SelectionRect | null;
connectionNodeId: string | null;
connectionHandleId: string | null;
connectionHandleType: HandleType | null;
connectionPosition: XYPosition;
connectionStatus: ConnectionStatus | null;
connectionMode: ConnectionMode;
snapToGrid: boolean;
snapGrid: SnapGrid;
nodesDraggable: boolean;
nodesConnectable: boolean;
nodesFocusable: boolean;
edgesFocusable: boolean;
elementsSelectable: boolean;
elevateNodesOnSelect: boolean;
multiSelectionActive: boolean;
connectionStartHandle: StartHandle | null;
onNodeDragStart?: NodeDragHandler;
onNodeDrag?: NodeDragHandler;
onNodeDragStop?: NodeDragHandler;
onSelectionDragStart?: SelectionDragHandler;
onSelectionDrag?: SelectionDragHandler;
onSelectionDragStop?: SelectionDragHandler;
onConnect?: OnConnect;
onConnectStart?: OnConnectStart;
onConnectEnd?: OnConnectEnd;
onClickConnectStart?: OnConnectStart;
onClickConnectEnd?: OnConnectEnd;
connectOnClick: boolean;
defaultEdgeOptions?: DefaultEdgeOptions;
fitViewOnInit: boolean;
fitViewOnInitDone: boolean;
fitViewOnInitOptions: FitViewOptions | undefined;
onNodesDelete?: OnNodesDelete;
onEdgesDelete?: OnEdgesDelete;
onError?: OnError;
// event handlers
onViewportChangeStart?: OnViewportChange;
onViewportChange?: OnViewportChange;
onViewportChangeEnd?: OnViewportChange;
onSelectionChange?: OnSelectionChangeFunc;
ariaLiveMessage: string;
autoPanOnConnect: boolean;
autoPanOnNodeDrag: boolean;
connectionRadius: number;
};
export type ReactFlowActions = {
setNodes: (nodes: Node[]) => void;
getNodes: () => Node[];
setEdges: (edges: Edge[]) => void;
setDefaultNodesAndEdges: (nodes?: Node[], edges?: Edge[]) => void;
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
updateNodePositions: (nodeDragItems: NodeDragItem[] | Node[], positionChanged: boolean, dragging: boolean) => void;
resetSelectedElements: () => void;
unselectNodesAndEdges: (params?: UnselectNodesAndEdgesParams) => void;
addSelectedNodes: (nodeIds: string[]) => void;
addSelectedEdges: (edgeIds: string[]) => void;
setMinZoom: (minZoom: number) => void;
setMaxZoom: (maxZoom: number) => void;
setTranslateExtent: (translateExtent: CoordinateExtent) => void;
setNodeExtent: (nodeExtent: CoordinateExtent) => void;
cancelConnection: () => void;
reset: () => void;
triggerNodeChanges: (changes: NodeChange[]) => void;
panBy: (delta: XYPosition) => void;
};
export type ReactFlowState = ReactFlowStore & ReactFlowActions;
export type UpdateNodeInternals = (nodeId: string) => void; export type UpdateNodeInternals = (nodeId: string) => void;
export type OnSelectionChangeParams = {
nodes: Node[];
edges: Edge[];
};
export type OnSelectionChangeFunc = (params: OnSelectionChangeParams) => void;
export type PanelPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'; export type PanelPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
export type ProOptions = { export type ProOptions = {
+9 -8
View File
@@ -2,22 +2,23 @@ import type { XYPosition, Position, Dimensions, OnConnect, Connection } from '.'
export type HandleType = 'source' | 'target'; export type HandleType = 'source' | 'target';
export interface HandleElement extends XYPosition, Dimensions { export type HandleElement = XYPosition &
id?: string | null; Dimensions & {
position: Position; id?: string | null;
} position: Position;
};
export interface StartHandle { export type StartHandle = {
nodeId: string; nodeId: string;
type: HandleType; type: HandleType;
handleId?: string | null; handleId?: string | null;
} };
export interface HandleProps { export type HandleProps = {
type: HandleType; type: HandleType;
position: Position; position: Position;
isConnectable?: boolean; isConnectable?: boolean;
onConnect?: OnConnect; onConnect?: OnConnect;
isValidConnection?: (connection: Connection) => boolean; isValidConnection?: (connection: Connection) => boolean;
id?: string; id?: string;
} };
-3
View File
@@ -2,7 +2,4 @@ export * from './general';
export * from './nodes'; export * from './nodes';
export * from './edges'; export * from './edges';
export * from './handles'; export * from './handles';
export * from './changes';
export * from './utils'; export * from './utils';
export * from './instance';
export * from './component-props';
+12 -46
View File
@@ -1,17 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
import { internalsSymbol } from '../'; import { internalsSymbol } from '../';
import type { XYPosition, Position, CoordinateExtent, HandleElement } from '.'; import type { XYPosition, Position, CoordinateExtent, HandleElement } from '.';
// interface for the user node items // this is stuff that all nodes share independent of the framework
export type Node<T = any, U extends string | undefined = string | undefined> = { export type BaseNode<T = any, U extends string | undefined = string | undefined> = {
id: string; id: string;
position: XYPosition; position: XYPosition;
data: T; data: T;
type?: U; type?: U;
style?: CSSProperties;
className?: string;
sourcePosition?: Position; sourcePosition?: Position;
targetPosition?: Position; targetPosition?: Position;
hidden?: boolean; hidden?: boolean;
@@ -31,7 +27,6 @@ export type Node<T = any, U extends string | undefined = string | undefined> = {
positionAbsolute?: XYPosition; positionAbsolute?: XYPosition;
ariaLabel?: string; ariaLabel?: string;
focusable?: boolean; focusable?: boolean;
resizing?: boolean;
// only used internally // only used internally
[internalsSymbol]?: { [internalsSymbol]?: {
@@ -41,44 +36,17 @@ export type Node<T = any, U extends string | undefined = string | undefined> = {
}; };
}; };
export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void;
export type NodeDragHandler = (event: ReactMouseEvent, node: Node, nodes: Node[]) => void;
export type SelectionDragHandler = (event: ReactMouseEvent, nodes: Node[]) => void;
export type WrapNodeProps<T = any> = Pick<
Node<T>,
'id' | 'data' | 'style' | 'className' | 'dragHandle' | 'sourcePosition' | 'targetPosition' | 'hidden' | 'ariaLabel'
> &
Required<Pick<Node<T>, 'selected' | 'type' | 'zIndex'>> & {
isConnectable: boolean;
xPos: number;
yPos: number;
xPosOrigin: number;
yPosOrigin: number;
initialized: boolean;
isSelectable: boolean;
isDraggable: boolean;
isFocusable: boolean;
selectNodesOnDrag: boolean;
onClick?: NodeMouseHandler;
onDoubleClick?: NodeMouseHandler;
onMouseEnter?: NodeMouseHandler;
onMouseMove?: NodeMouseHandler;
onMouseLeave?: NodeMouseHandler;
onContextMenu?: NodeMouseHandler;
resizeObserver: ResizeObserver | null;
isParent: boolean;
noDragClassName: string;
noPanClassName: string;
rfId: string;
disableKeyboardA11y: boolean;
};
// props that get passed to a custom node // props that get passed to a custom node
export type NodeProps<T = any> = Pick< export type NodeProps<T = any> = {
WrapNodeProps<T>, id: BaseNode['id'];
'id' | 'data' | 'dragHandle' | 'type' | 'selected' | 'isConnectable' | 'xPos' | 'yPos' | 'zIndex' data: T;
> & { dragHandle: BaseNode['dragHandle'];
type: BaseNode['type'];
selected: BaseNode['selected'];
isConnectable: BaseNode['connectable'];
zIndex: BaseNode['zIndex'];
xPos: number;
yPos: number;
dragging: boolean; dragging: boolean;
targetPosition?: Position; targetPosition?: Position;
sourcePosition?: Position; sourcePosition?: Position;
@@ -95,8 +63,6 @@ export type NodeDimensionUpdate = {
forceUpdate?: boolean; forceUpdate?: boolean;
}; };
export type NodeInternals = Map<string, Node>;
export type NodeBounds = XYPosition & { export type NodeBounds = XYPosition & {
width: number | null; width: number | null;
height: number | null; height: number | null;
+7 -7
View File
@@ -5,24 +5,24 @@ export enum Position {
Bottom = 'bottom', Bottom = 'bottom',
} }
export interface XYPosition { export type XYPosition = {
x: number; x: number;
y: number; y: number;
} };
export type XYZPosition = XYPosition & { z: number }; export type XYZPosition = XYPosition & { z: number };
export interface Dimensions { export type Dimensions = {
width: number; width: number;
height: number; height: number;
} };
export interface Rect extends Dimensions, XYPosition {} export type Rect = Dimensions & XYPosition;
export interface Box extends XYPosition { export type Box = XYPosition & {
x2: number; x2: number;
y2: number; y2: number;
} };
export type Transform = [number, number, number]; export type Transform = [number, number, number];
+63 -44
View File
@@ -5,27 +5,32 @@ import { zoomIdentity } from 'd3-zoom';
import { boxToRect, clamp, devWarn, getBoundsOfBoxes, getOverlappingArea, rectToBox } from './utils'; import { boxToRect, clamp, devWarn, getBoundsOfBoxes, getOverlappingArea, rectToBox } from './utils';
import { import {
errorMessages, errorMessages,
type Node,
type Edge,
type Connection, type Connection,
type EdgeMarkerType, type EdgeMarkerType,
type Transform, type Transform,
type XYPosition, type XYPosition,
type Rect, type Rect,
type NodeInternals,
type NodeOrigin, type NodeOrigin,
FitViewParams, BaseNode,
FitViewOptions, BaseEdge,
FitViewParamsBase,
FitViewOptionsBase,
} from '@reactflow/system'; } from '@reactflow/system';
export const isEdge = (element: Node | Connection | Edge): element is Edge => export const isEdgeBase = <NodeType extends BaseNode = BaseNode, EdgeType extends BaseEdge = BaseEdge>(
'id' in element && 'source' in element && 'target' in element; element: NodeType | Connection | EdgeType
): element is EdgeType => 'id' in element && 'source' in element && 'target' in element;
export const isNode = (element: Node | Connection | Edge): element is Node => export const isNodeBase = <NodeType extends BaseNode = BaseNode, EdgeType extends BaseEdge = BaseEdge>(
'id' in element && !('source' in element) && !('target' in element); element: NodeType | Connection | EdgeType
): element is NodeType => 'id' in element && !('source' in element) && !('target' in element);
export const getOutgoers = <T = any, U extends T = T>(node: Node<U>, nodes: Node<T>[], edges: Edge[]): Node<T>[] => { export const getOutgoersBase = <NodeType extends BaseNode = BaseNode, EdgeType extends BaseEdge = BaseEdge>(
if (!isNode(node)) { node: NodeType,
nodes: NodeType[],
edges: EdgeType[]
): NodeType[] => {
if (!isNodeBase(node)) {
return []; return [];
} }
@@ -33,8 +38,12 @@ export const getOutgoers = <T = any, U extends T = T>(node: Node<U>, nodes: Node
return nodes.filter((n) => outgoerIds.includes(n.id)); return nodes.filter((n) => outgoerIds.includes(n.id));
}; };
export const getIncomers = <T = any, U extends T = T>(node: Node<U>, nodes: Node<T>[], edges: Edge[]): Node<T>[] => { export const getIncomersBase = <NodeType extends BaseNode = BaseNode, EdgeType extends BaseEdge = BaseEdge>(
if (!isNode(node)) { node: NodeType,
nodes: NodeType[],
edges: EdgeType[]
): NodeType[] => {
if (!isNodeBase(node)) {
return []; return [];
} }
@@ -42,7 +51,7 @@ export const getIncomers = <T = any, U extends T = T>(node: Node<U>, nodes: Node
return nodes.filter((n) => incomersIds.includes(n.id)); return nodes.filter((n) => incomersIds.includes(n.id));
}; };
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection): string => const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection | BaseEdge): string =>
`reactflow__edge-${source}${sourceHandle || ''}-${target}${targetHandle || ''}`; `reactflow__edge-${source}${sourceHandle || ''}-${target}${targetHandle || ''}`;
export const getMarkerId = (marker: EdgeMarkerType | undefined, rfId?: string): string => { export const getMarkerId = (marker: EdgeMarkerType | undefined, rfId?: string): string => {
@@ -62,7 +71,7 @@ export const getMarkerId = (marker: EdgeMarkerType | undefined, rfId?: string):
.join('&')}`; .join('&')}`;
}; };
const connectionExists = (edge: Edge, edges: Edge[]) => { const connectionExists = (edge: BaseEdge, edges: BaseEdge[]) => {
return edges.some( return edges.some(
(el) => (el) =>
el.source === edge.source && el.source === edge.source &&
@@ -72,21 +81,24 @@ const connectionExists = (edge: Edge, edges: Edge[]) => {
); );
}; };
export const addEdge = (edgeParams: Edge | Connection, edges: Edge[]): Edge[] => { export const addEdgeBase = <EdgeType extends BaseEdge>(
edgeParams: EdgeType | Connection,
edges: EdgeType[]
): EdgeType[] => {
if (!edgeParams.source || !edgeParams.target) { if (!edgeParams.source || !edgeParams.target) {
devWarn('006', errorMessages['006']()); devWarn('006', errorMessages['006']());
return edges; return edges;
} }
let edge: Edge; let edge: EdgeType;
if (isEdge(edgeParams)) { if (isEdgeBase(edgeParams)) {
edge = { ...edgeParams }; edge = { ...edgeParams };
} else { } else {
edge = { edge = {
...edgeParams, ...edgeParams,
id: getEdgeId(edgeParams), id: getEdgeId(edgeParams),
} as Edge; } as EdgeType;
} }
if (connectionExists(edge, edges)) { if (connectionExists(edge, edges)) {
@@ -96,14 +108,18 @@ export const addEdge = (edgeParams: Edge | Connection, edges: Edge[]): Edge[] =>
return edges.concat(edge); return edges.concat(edge);
}; };
export const updateEdge = (oldEdge: Edge, newConnection: Connection, edges: Edge[]): Edge[] => { export const updateEdgeBase = <EdgeType extends BaseEdge>(
oldEdge: EdgeType,
newConnection: Connection,
edges: EdgeType[]
): EdgeType[] => {
if (!newConnection.source || !newConnection.target) { if (!newConnection.source || !newConnection.target) {
devWarn('006', errorMessages['006']()); devWarn('006', errorMessages['006']());
return edges; return edges;
} }
const foundEdge = edges.find((e) => e.id === oldEdge.id) as Edge; const foundEdge = edges.find((e) => e.id === oldEdge.id) as EdgeType;
if (!foundEdge) { if (!foundEdge) {
devWarn('007', errorMessages['007'](oldEdge.id)); devWarn('007', errorMessages['007'](oldEdge.id));
@@ -119,7 +135,7 @@ export const updateEdge = (oldEdge: Edge, newConnection: Connection, edges: Edge
target: newConnection.target, target: newConnection.target,
sourceHandle: newConnection.sourceHandle, sourceHandle: newConnection.sourceHandle,
targetHandle: newConnection.targetHandle, targetHandle: newConnection.targetHandle,
} as Edge; } as EdgeType;
return edges.filter((e) => e.id !== oldEdge.id).concat(edge); return edges.filter((e) => e.id !== oldEdge.id).concat(edge);
}; };
@@ -153,7 +169,7 @@ export const rendererPointToPoint = ({ x, y }: XYPosition, [tx, ty, tScale]: Tra
}; };
export const getNodePositionWithOrigin = ( export const getNodePositionWithOrigin = (
node: Node | undefined, node: BaseNode | undefined,
nodeOrigin: NodeOrigin = [0, 0] nodeOrigin: NodeOrigin = [0, 0]
): XYPosition & { positionAbsolute: XYPosition } => { ): XYPosition & { positionAbsolute: XYPosition } => {
if (!node) { if (!node) {
@@ -186,7 +202,7 @@ export const getNodePositionWithOrigin = (
}; };
}; };
export const getRectOfNodes = (nodes: Node[], nodeOrigin: NodeOrigin = [0, 0]): Rect => { export const getRectOfNodes = (nodes: BaseNode[], nodeOrigin: NodeOrigin = [0, 0]): Rect => {
if (nodes.length === 0) { if (nodes.length === 0) {
return { x: 0, y: 0, width: 0, height: 0 }; return { x: 0, y: 0, width: 0, height: 0 };
} }
@@ -210,15 +226,15 @@ export const getRectOfNodes = (nodes: Node[], nodeOrigin: NodeOrigin = [0, 0]):
return boxToRect(box); return boxToRect(box);
}; };
export const getNodesInside = ( export const getNodesInside = <NodeType extends BaseNode>(
nodeInternals: NodeInternals, nodes: NodeType[],
rect: Rect, rect: Rect,
[tx, ty, tScale]: Transform = [0, 0, 1], [tx, ty, tScale]: Transform = [0, 0, 1],
partially = false, partially = false,
// set excludeNonSelectableNodes if you want to pay attention to the nodes "selectable" attribute // set excludeNonSelectableNodes if you want to pay attention to the nodes "selectable" attribute
excludeNonSelectableNodes = false, excludeNonSelectableNodes = false,
nodeOrigin: NodeOrigin = [0, 0] nodeOrigin: NodeOrigin = [0, 0]
): Node[] => { ): NodeType[] => {
const paneRect = { const paneRect = {
x: (rect.x - tx) / tScale, x: (rect.x - tx) / tScale,
y: (rect.y - ty) / tScale, y: (rect.y - ty) / tScale,
@@ -226,13 +242,11 @@ export const getNodesInside = (
height: rect.height / tScale, height: rect.height / tScale,
}; };
const visibleNodes: Node[] = []; const visibleNodes = nodes.reduce<NodeType[]>((res, node) => {
nodeInternals.forEach((node) => {
const { width, height, selectable = true, hidden = false } = node; const { width, height, selectable = true, hidden = false } = node;
if ((excludeNonSelectableNodes && !selectable) || hidden) { if ((excludeNonSelectableNodes && !selectable) || hidden) {
return false; return res;
} }
const { positionAbsolute } = getNodePositionWithOrigin(node, nodeOrigin); const { positionAbsolute } = getNodePositionWithOrigin(node, nodeOrigin);
@@ -252,14 +266,19 @@ export const getNodesInside = (
const isVisible = notInitialized || partiallyVisible || overlappingArea >= area; const isVisible = notInitialized || partiallyVisible || overlappingArea >= area;
if (isVisible || node.dragging) { if (isVisible || node.dragging) {
visibleNodes.push(node); res.push(node);
} }
});
return res;
}, []);
return visibleNodes; return visibleNodes;
}; };
export const getConnectedEdges = (nodes: Node[], edges: Edge[]): Edge[] => { export const getConnectedEdgesBase = <NodeType extends BaseNode = BaseNode, EdgeType extends BaseEdge = BaseEdge>(
nodes: NodeType[],
edges: EdgeType[]
): EdgeType[] => {
const nodeIds = nodes.map((node) => node.id); const nodeIds = nodes.map((node) => node.id);
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target)); return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target));
@@ -289,15 +308,15 @@ export const getD3Transition = (selection: D3Selection<Element, unknown, null, u
return selection.transition().duration(duration); return selection.transition().duration(duration);
}; };
export function fitView( export function fitView<Params extends FitViewParamsBase<BaseNode>, Options extends FitViewOptionsBase<BaseNode>>(
{ nodes, width, height, d3Zoom, d3Selection, nodeOrigin, minZoom, maxZoom }: FitViewParams, { nodes, width, height, d3Zoom, d3Selection, nodeOrigin, minZoom, maxZoom }: Params,
options: FitViewOptions = {} options?: Options
) { ) {
const filteredNodes = nodes.filter((n) => { const filteredNodes = nodes.filter((n) => {
const isVisible = options.includeHiddenNodes ? n.width && n.height : !n.hidden; const isVisible = options?.includeHiddenNodes ? n.width && n.height : !n.hidden;
if (options.nodes?.length) { if (options?.nodes?.length) {
return isVisible && options.nodes.some((optionNode) => optionNode.id === n.id); return isVisible && options?.nodes.some((optionNode) => optionNode.id === n.id);
} }
return isVisible; return isVisible;
@@ -312,14 +331,14 @@ export function fitView(
bounds, bounds,
width, width,
height, height,
options.minZoom ?? minZoom, options?.minZoom ?? minZoom,
options.maxZoom ?? maxZoom, options?.maxZoom ?? maxZoom,
options.padding ?? 0.1 options?.padding ?? 0.1
); );
const nextTransform = zoomIdentity.translate(x, y).scale(zoom); const nextTransform = zoomIdentity.translate(x, y).scale(zoom);
if (typeof options.duration === 'number' && options.duration > 0) { if (typeof options?.duration === 'number' && options.duration > 0) {
d3Zoom.transform(getD3Transition(d3Selection, options.duration), nextTransform); d3Zoom.transform(getD3Transition(d3Selection, options.duration), nextTransform);
} else { } else {
d3Zoom.transform(d3Selection, nextTransform); d3Zoom.transform(d3Selection, nextTransform);
+7 -2
View File
@@ -3,7 +3,7 @@ import type {
MouseEvent as ReactMouseEvent, MouseEvent as ReactMouseEvent,
TouchEvent as ReactTouchEvent, TouchEvent as ReactTouchEvent,
} from 'react'; } from 'react';
import type { Dimensions, Node, XYPosition, CoordinateExtent, Box, Rect } from '@reactflow/system'; import type { Dimensions, XYPosition, CoordinateExtent, Box, Rect, BaseNode } from '@reactflow/system';
export const getDimensions = (node: HTMLDivElement): Dimensions => ({ export const getDimensions = (node: HTMLDivElement): Dimensions => ({
width: node.offsetWidth, width: node.offsetWidth,
@@ -60,7 +60,7 @@ export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
height: y2 - y, height: y2 - y,
}); });
export const nodeToRect = (node: Node): Rect => ({ export const nodeToRect = (node: BaseNode): Rect => ({
...(node.positionAbsolute || { x: 0, y: 0 }), ...(node.positionAbsolute || { x: 0, y: 0 }),
width: node.width || 0, width: node.width || 0,
height: node.height || 0, height: node.height || 0,
@@ -125,3 +125,8 @@ export const getEventPosition = (
y: evtY - (bounds?.top ?? 0), y: evtY - (bounds?.top ?? 0),
}; };
}; };
export const infiniteExtent: CoordinateExtent = [
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
];