feat(packages): add @reactflow/system and @reactflow/utils

This commit is contained in:
moklick
2023-02-16 21:54:09 +01:00
parent a12c893226
commit 3920fca670
90 changed files with 375 additions and 1152 deletions
@@ -1,6 +1,7 @@
import { CSSProperties } from 'react';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore';
import type { ReactFlowState } from '../../types';
const style: CSSProperties = { display: 'none' };
const ariaLiveStyle: CSSProperties = {
@@ -1,5 +1,6 @@
import type { PanelPosition, ProOptions } from '@reactflow/system';
import Panel from '../Panel';
import type { PanelPosition, ProOptions } from '../../types';
type AttributionProps = {
proOptions?: ProOptions;
@@ -1,20 +1,22 @@
import { CSSProperties, useCallback } from 'react';
import { shallow } from 'zustand/shallow';
import cc from 'classcat';
import { internalsSymbol } from '@reactflow/system';
import {
internalsSymbol,
Position,
ConnectionLineType,
ConnectionMode,
type ConnectionLineComponent,
type ConnectionStatus,
type HandleType,
type ReactFlowState,
type ReactFlowStore,
} from '@reactflow/system';
import { useStore } from '../../hooks/useStore';
import { getBezierPath } from '../Edges/BezierEdge';
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge';
import type {
ConnectionLineComponent,
ConnectionStatus,
HandleType,
ReactFlowState,
ReactFlowStore,
} from '../../types';
import { Position, ConnectionLineType, ConnectionMode } from '../../types';
type ConnectionLineProps = {
nodeId: string;
@@ -1,8 +1,8 @@
import type { ReactNode } from 'react';
import { createPortal } from 'react-dom';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore';
import { ReactFlowState } from '../../types';
const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
@@ -1,6 +1,7 @@
import { isNumeric } from '@reactflow/utils';
import type { BaseEdgeProps } from '@reactflow/system';
import EdgeText from './EdgeText';
import { isNumeric } from '../../utils';
import type { BaseEdgeProps } from '../../types';
const BaseEdge = ({
path,
@@ -1,9 +1,8 @@
import { memo } from 'react';
import { Position, type BezierEdgeProps } from '@reactflow/system';
import BaseEdge from './BaseEdge';
import { getBezierEdgeCenter } from './utils';
import { Position } from '../../types';
import type { BezierEdgeProps } from '../../types';
export interface GetBezierPathParams {
sourceX: number;
@@ -1,7 +1,6 @@
import type { FC, MouseEvent as ReactMouseEvent, SVGAttributes } from 'react';
import cc from 'classcat';
import { Position } from '../../types';
import { Position } from '@reactflow/system';
const shiftX = (x: number, shift: number, position: Position): number => {
if (position === Position.Left) return x - shift;
@@ -1,8 +1,7 @@
import { memo, useRef, useState, useEffect } from 'react';
import type { FC, PropsWithChildren } from 'react';
import cc from 'classcat';
import { EdgeTextProps, Rect } from '../../types';
import { EdgeTextProps, Rect } from '@reactflow/system';
const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({
x,
@@ -1,9 +1,8 @@
import { memo } from 'react';
import { Position, type EdgeProps } from '@reactflow/system';
import BaseEdge from './BaseEdge';
import { getBezierEdgeCenter } from './utils';
import { Position } from '../../types';
import type { EdgeProps } from '../../types';
export interface GetSimpleBezierPathParams {
sourceX: number;
@@ -1,9 +1,8 @@
import { memo } from 'react';
import { Position, type SmoothStepEdgeProps, type XYPosition } from '@reactflow/system';
import BaseEdge from './BaseEdge';
import { getEdgeCenter } from './utils';
import { Position } from '../../types';
import type { SmoothStepEdgeProps, XYPosition } from '../../types';
export interface GetSmoothStepPathParams {
sourceX: number;
@@ -1,7 +1,7 @@
import { memo, useMemo } from 'react';
import type { SmoothStepEdgeProps } from '@reactflow/system';
import SmoothStepEdge from './SmoothStepEdge';
import type { SmoothStepEdgeProps } from '../../types';
const StepEdge = memo((props: SmoothStepEdgeProps) => (
<SmoothStepEdge
@@ -1,8 +1,8 @@
import { memo } from 'react';
import type { EdgeProps } from '@reactflow/system';
import BaseEdge from './BaseEdge';
import { getEdgeCenter } from './utils';
import type { EdgeProps } from '../../types';
export type GetStraightPathParams = {
sourceX: number;
+1 -2
View File
@@ -1,7 +1,6 @@
import { MouseEvent as ReactMouseEvent } from 'react';
import { StoreApi } from 'zustand';
import type { Edge, MarkerType, ReactFlowState } from '../../types';
import type { Edge, MarkerType, ReactFlowState } from '@reactflow/system';
export const getMarkerEnd = (markerType?: MarkerType, markerEndId?: string): string => {
if (typeof markerEndId !== 'undefined' && markerEndId) {
@@ -1,15 +1,14 @@
import { memo, useState, useMemo, useRef } from 'react';
import type { ComponentType, KeyboardEvent } from 'react';
import cc from 'classcat';
import { getMarkerId, elementSelectionKeys } from '@reactflow/utils';
import type { EdgeProps, WrapEdgeProps, Connection } from '@reactflow/system';
import { useStoreApi } from '../../hooks/useStore';
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
import { handlePointerDown } from '../Handle/handler';
import { EdgeAnchor } from './EdgeAnchor';
import { getMarkerId } from '../../utils/graph';
import { getMouseHandler } from './utils';
import { elementSelectionKeys } from '../../utils';
import type { EdgeProps, WrapEdgeProps, Connection } from '../../types';
export default (EdgeComponent: ComponentType<EdgeProps>) => {
const EdgeWrapper = ({
@@ -1,9 +1,14 @@
import type { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
import { StoreApi } from 'zustand';
import {
getHostForElement,
calcAutoPan,
getEventPosition,
pointToRendererPoint,
rendererPointToPoint,
} from '@reactflow/utils';
import type { OnConnect, HandleType, ReactFlowState, Connection } from '@reactflow/system';
import { getHostForElement, calcAutoPan, getEventPosition } from '../../utils';
import type { OnConnect, HandleType, ReactFlowState, Connection } from '../../types';
import { pointToRendererPoint, rendererPointToPoint } from '../../utils/graph';
import {
ConnectionHandle,
getClosestHandle,
@@ -1,16 +1,13 @@
import { memo, HTMLAttributes, forwardRef, MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
import cc from 'classcat';
import { shallow } from 'zustand/shallow';
import { errorMessages, Position, type HandleProps, type Connection, type ReactFlowState } from '@reactflow/system';
import { getHostForElement, isMouseEvent, addEdge } from '@reactflow/utils';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { useNodeId } from '../../contexts/NodeIdContext';
import { handlePointerDown } from './handler';
import { getHostForElement, isMouseEvent } from '../../utils';
import { addEdge } from '../../utils/graph';
import { Position } from '../../types';
import type { HandleProps, Connection, ReactFlowState } from '../../types';
import { isValidHandle } from './utils';
import { errorMessages } from '../../contants';
const alwaysValid = () => true;
+3 -5
View File
@@ -1,9 +1,7 @@
import { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
import { internalsSymbol } from '@reactflow/system';
import { ConnectionMode, ConnectionStatus } from '../../types';
import { getEventPosition } from '../../utils';
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '../../types';
import { internalsSymbol, ConnectionMode, ConnectionStatus } from '@reactflow/system';
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '@reactflow/system';
import { getEventPosition } from '@reactflow/utils';
export type ConnectionHandle = {
id: string | null;
@@ -1,8 +1,7 @@
import { memo } from 'react';
import { Position, type NodeProps } from '@reactflow/system';
import Handle from '../../components/Handle';
import { Position } from '../../types';
import type { NodeProps } from '../../types';
const DefaultNode = ({
data,
@@ -1,8 +1,7 @@
import { memo } from 'react';
import { Position, type NodeProps } from '@reactflow/system';
import Handle from '../../components/Handle';
import { Position } from '../../types';
import type { NodeProps } from '../../types';
const InputNode = ({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) => (
<>
@@ -1,8 +1,7 @@
import { memo } from 'react';
import { Position, type NodeProps } from '@reactflow/system';
import Handle from '../../components/Handle';
import { Position } from '../../types';
import type { NodeProps } from '../../types';
const OutputNode = ({ data, isConnectable, targetPosition = Position.Top }: NodeProps) => (
<>
+2 -4
View File
@@ -1,9 +1,7 @@
import { MouseEvent } from 'react';
import { StoreApi } from 'zustand';
import { getDimensions } from '../../utils';
import { Position } from '../../types';
import type { HandleElement, Node, NodeOrigin, ReactFlowState } from '../../types';
import { getDimensions } from '@reactflow/utils';
import { Position, type HandleElement, type Node, type NodeOrigin, type ReactFlowState } from '@reactflow/system';
export const getHandleBounds = (
selector: string,
@@ -1,6 +1,8 @@
import { useEffect, useRef, memo } from 'react';
import type { ComponentType, MouseEvent, KeyboardEvent } from 'react';
import cc from 'classcat';
import { elementSelectionKeys, isInputDOMNode } from '@reactflow/utils';
import type { NodeProps, WrapNodeProps, XYPosition } from '@reactflow/system';
import { useStoreApi } from '../../hooks/useStore';
import { Provider } from '../../contexts/NodeIdContext';
@@ -8,8 +10,6 @@ import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
import useDrag from '../../hooks/useDrag';
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
import { getMouseHandler, handleNodeClick } from './utils';
import { elementSelectionKeys, isInputDOMNode } from '../../utils';
import type { NodeProps, WrapNodeProps, XYPosition } from '../../types';
export const arrowKeyDiffs: Record<string, XYPosition> = {
ArrowUp: { x: 0, y: -1 },
@@ -7,13 +7,13 @@ import { memo, useRef, useEffect } from 'react';
import type { MouseEvent, KeyboardEvent } from 'react';
import cc from 'classcat';
import { shallow } from 'zustand/shallow';
import { getRectOfNodes } from '@reactflow/utils';
import type { Node, ReactFlowState } from '@reactflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { getRectOfNodes } from '../../utils/graph';
import useDrag from '../../hooks/useDrag';
import { arrowKeyDiffs } from '../Nodes/wrapNode';
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
import type { Node, ReactFlowState } from '../../types';
export interface NodesSelectionProps {
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
+1 -1
View File
@@ -1,8 +1,8 @@
import type { HTMLAttributes, ReactNode } from 'react';
import cc from 'classcat';
import type { PanelPosition, ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore';
import type { PanelPosition, ReactFlowState } from '../../types';
export type PanelProps = HTMLAttributes<HTMLDivElement> & {
position: PanelPosition;
@@ -1,10 +1,10 @@
import { useRef } from 'react';
import type { FC, PropsWithChildren } from 'react';
import { StoreApi } from 'zustand';
import type { ReactFlowState } from '@reactflow/system';
import { Provider } from '../../contexts/RFStoreContext';
import { createRFStore } from '../../store';
import type { ReactFlowState } from '../../types';
const ReactFlowProvider: FC<PropsWithChildren<unknown>> = ({ children }) => {
const storeRef = useRef<StoreApi<ReactFlowState> | null>(null);
@@ -1,8 +1,8 @@
import { memo, useEffect } from 'react';
import { shallow } from 'zustand/shallow';
import type { ReactFlowState, OnSelectionChangeFunc, Node, Edge } from '@reactflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore';
import type { ReactFlowState, OnSelectionChangeFunc, Node, Edge } from '../../types';
type SelectionListenerProps = {
onSelectionChange?: OnSelectionChangeFunc;
@@ -1,9 +1,9 @@
import { useEffect } from 'react';
import { StoreApi } from 'zustand';
import { shallow } from 'zustand/shallow';
import type { Node, Edge, ReactFlowState, CoordinateExtent, ReactFlowProps, ReactFlowStore } from '@reactflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore';
import type { Node, Edge, ReactFlowState, CoordinateExtent, ReactFlowProps, ReactFlowStore } from '../../types';
type StoreUpdaterProps = Pick<
ReactFlowProps,
@@ -1,7 +1,7 @@
import { shallow } from 'zustand/shallow';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore';
import type { ReactFlowState } from '../../types';
const selector = (s: ReactFlowState) => ({
userSelectionActive: s.userSelectionActive,
@@ -1,9 +1,9 @@
import { memo, useCallback } from 'react';
import type { EdgeMarker, ReactFlowState } from '@reactflow/system';
import { getMarkerId } from '@reactflow/utils';
import { useStore } from '../../hooks/useStore';
import { getMarkerId } from '../../utils/graph';
import { useMarkerSymbol } from './MarkerSymbols';
import type { EdgeMarker, ReactFlowState } from '../../types';
type MarkerProps = EdgeMarker & {
id: string;
@@ -1,9 +1,7 @@
import { useMemo } from 'react';
import { errorMessages, MarkerType, type EdgeMarker } from '@reactflow/system';
import { MarkerType } from '../../types';
import type { EdgeMarker } from '../../types';
import { useStoreApi } from '../../hooks/useStore';
import { errorMessages } from '../../contants';
type SymbolProps = Omit<EdgeMarker, 'type'>;
@@ -1,6 +1,7 @@
import { memo, ReactNode } from 'react';
import { shallow } from 'zustand/shallow';
import cc from 'classcat';
import { errorMessages, ConnectionMode, Position, type Edge, type ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore';
import useVisibleEdges from '../../hooks/useVisibleEdges';
@@ -8,9 +9,6 @@ import MarkerDefinitions from './MarkerDefinitions';
import { getEdgePositions, getHandle, getNodeData } from './utils';
import { GraphViewProps } from '../GraphView';
import { ConnectionMode, Position } from '../../types';
import type { Edge, ReactFlowState } from '../../types';
import { errorMessages } from '../../contants';
type EdgeRendererProps = Pick<
GraphViewProps,
@@ -1,21 +1,21 @@
import type { ComponentType } from 'react';
import { internalsSymbol } from '@reactflow/system';
import {
internalsSymbol,
Position,
type EdgeProps,
type EdgeTypes,
type EdgeTypesWrapped,
type HandleElement,
type NodeHandleBounds,
type Node,
type Rect,
type Transform,
type XYPosition,
} from '@reactflow/system';
import { rectToBox } from '@reactflow/utils';
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges';
import wrapEdge from '../../components/Edges/wrapEdge';
import { rectToBox } from '../../utils';
import { Position } from '../../types';
import type {
EdgeProps,
EdgeTypes,
EdgeTypesWrapped,
HandleElement,
NodeHandleBounds,
Node,
Rect,
Transform,
XYPosition,
} from '../../types';
export type CreateEdgeTypes = (edgeTypes: EdgeTypes) => EdgeTypesWrapped;
@@ -1,5 +1,5 @@
import { memo } from 'react';
import type { ReactNode } from 'react';
import { memo, type ReactNode } from 'react';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../../hooks/useStore';
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
@@ -8,7 +8,6 @@ import { GraphViewProps } from '../GraphView';
import ZoomPane from '../ZoomPane';
import Pane from '../Pane';
import NodesSelection from '../../components/NodesSelection';
import type { ReactFlowState } from '../../types';
export type FlowRendererProps = Omit<
GraphViewProps,
@@ -1,4 +1,5 @@
import { memo } from 'react';
import type { EdgeTypesWrapped, NodeTypesWrapped, ReactFlowProps } from '@reactflow/system';
import FlowRenderer from '../FlowRenderer';
import NodeRenderer from '../NodeRenderer';
@@ -6,7 +7,6 @@ import EdgeRenderer from '../EdgeRenderer';
import ViewportWrapper from '../Viewport';
import useOnInitHandler from '../../hooks/useOnInitHandler';
import ConnectionLine from '../../components/ConnectionLine';
import type { EdgeTypesWrapped, NodeTypesWrapped, ReactFlowProps } from '../../types';
export type GraphViewProps = Omit<ReactFlowProps, 'onSelectionChange' | 'nodes' | 'edges' | 'nodeTypes' | 'edgeTypes'> &
Required<
@@ -1,17 +1,14 @@
import { memo, useMemo, useEffect, useRef } from 'react';
import type { ComponentType } from 'react';
import { shallow } from 'zustand/shallow';
import { internalsSymbol } from '@reactflow/system';
import { internalsSymbol, errorMessages, Position, type ReactFlowState, type WrapNodeProps } from '@reactflow/system';
import { clampPosition } from '@reactflow/utils';
import useVisibleNodes from '../../hooks/useVisibleNodes';
import { useStore } from '../../hooks/useStore';
import { clampPosition } from '../../utils';
import { containerStyle } from '../../styles';
import { GraphViewProps } from '../GraphView';
import { getPositionWithOrigin } from './utils';
import { Position } from '../../types';
import type { ReactFlowState, WrapNodeProps } from '../../types';
import { errorMessages } from '../../contants';
type NodeRendererProps = Pick<
GraphViewProps,
@@ -1,11 +1,11 @@
import type { ComponentType } from 'react';
import type { NodeTypes, NodeProps, NodeTypesWrapped, NodeOrigin, XYPosition } from '@reactflow/system';
import DefaultNode from '../../components/Nodes/DefaultNode';
import InputNode from '../../components/Nodes/InputNode';
import OutputNode from '../../components/Nodes/OutputNode';
import GroupNode from '../../components/Nodes/GroupNode';
import wrapNode from '../../components/Nodes/wrapNode';
import type { NodeTypes, NodeProps, NodeTypesWrapped, NodeOrigin, XYPosition } from '../../types';
export type CreateNodeTypes = (nodeTypes: NodeTypes) => NodeTypesWrapped;
+8 -4
View File
@@ -5,15 +5,19 @@
import { memo, useRef, MouseEvent as ReactMouseEvent, ReactNode } from 'react';
import { shallow } from 'zustand/shallow';
import cc from 'classcat';
import { getConnectedEdges, getNodesInside, getEventPosition } from '@reactflow/utils';
import {
SelectionMode,
type ReactFlowProps,
type ReactFlowState,
type NodeChange,
type EdgeChange,
} from '@reactflow/system';
import UserSelection from '../../components/UserSelection';
import { containerStyle } from '../../styles';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { getSelectionChanges } from '../../utils/changes';
import { getConnectedEdges, getNodesInside } from '../../utils/graph';
import { getEventPosition } from '../../utils';
import { SelectionMode } from '../../types';
import type { ReactFlowProps, ReactFlowState, NodeChange, EdgeChange } from '../../types';
type PaneProps = {
isSelecting: boolean;
@@ -1,5 +1,4 @@
import { useContext } from 'react';
import type { FC, PropsWithChildren } from 'react';
import { useContext, type FC, type PropsWithChildren } from 'react';
import StoreContext from '../../contexts/RFStoreContext';
import ReactFlowProvider from '../../components/ReactFlowProvider';
+14 -11
View File
@@ -1,6 +1,20 @@
import { forwardRef } from 'react';
import type { CSSProperties } from 'react';
import cc from 'classcat';
import {
ConnectionLineType,
ConnectionMode,
PanOnScrollMode,
SelectionMode,
type EdgeTypes,
type EdgeTypesWrapped,
type NodeOrigin,
type NodeTypes,
type NodeTypesWrapped,
type ReactFlowProps,
type ReactFlowRefType,
type Viewport,
} from '@reactflow/system';
import Attribution from '../../components/Attribution';
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges';
@@ -17,17 +31,6 @@ import GraphView from '../GraphView';
import Wrapper from './Wrapper';
import { infiniteExtent } from '../../store/initialState';
import { useNodeOrEdgeTypes } from './utils';
import { ConnectionLineType, ConnectionMode, PanOnScrollMode, SelectionMode } from '../../types';
import type {
EdgeTypes,
EdgeTypesWrapped,
NodeOrigin,
NodeTypes,
NodeTypesWrapped,
ReactFlowProps,
ReactFlowRefType,
Viewport,
} from '../../types';
const defaultNodeTypes: NodeTypes = {
input: InputNode,
@@ -1,11 +1,16 @@
import { useMemo, useRef } from 'react';
import { shallow } from 'zustand/shallow';
import {
errorMessages,
type EdgeTypes,
type EdgeTypesWrapped,
type NodeTypes,
type NodeTypesWrapped,
} from '@reactflow/system';
import { devWarn } from '@reactflow/utils';
import { devWarn } from '../../utils';
import { CreateEdgeTypes } from '../EdgeRenderer/utils';
import { CreateNodeTypes } from '../NodeRenderer/utils';
import type { EdgeTypes, EdgeTypesWrapped, NodeTypes, NodeTypesWrapped } from '../../types';
import { errorMessages } from '../../contants';
export function useNodeOrEdgeTypes(nodeOrEdgeTypes: NodeTypes, createTypes: CreateNodeTypes): NodeTypesWrapped;
export function useNodeOrEdgeTypes(nodeOrEdgeTypes: EdgeTypes, createTypes: CreateEdgeTypes): EdgeTypesWrapped;
@@ -1,7 +1,7 @@
import type { ReactNode } from 'react';
import type { ReactFlowState } from '@reactflow/system';
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]})`;
@@ -4,15 +4,14 @@ import { zoom, zoomIdentity } from 'd3-zoom';
import type { D3ZoomEvent } from 'd3-zoom';
import { select, pointer } from 'd3-selection';
import { shallow } from 'zustand/shallow';
import { clamp } from '@reactflow/utils';
import { type Viewport, type ReactFlowState, CoordinateExtent, PanOnScrollMode } from '@reactflow/system';
import useKeyPress from '../../hooks/useKeyPress';
import useResizeHandler from '../../hooks/useResizeHandler';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { containerStyle } from '../../styles';
import { clamp } from '../../utils';
import { CoordinateExtent, PanOnScrollMode } from '../../types';
import type { FlowRendererProps } from '../FlowRenderer';
import type { Viewport, ReactFlowState } from '../../types';
type ZoomPaneProps = Omit<
FlowRendererProps,
-20
View File
@@ -1,20 +0,0 @@
import { Edge, HandleElement } from './types';
export const errorMessages = {
'001': () =>
'[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001',
'002': () =>
"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",
'003': (nodeType: string) => `Node type "${nodeType}" not found. Using fallback type "default".`,
'004': () => 'The React Flow parent container needs a width and a height to render the graph.',
'005': () => 'Only child nodes can use a parent extent.',
'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.`,
'009': (type: string) => `Marker type "${type}" doesn't exist.`,
'008': (sourceHandle: HandleElement | null, edge: Edge) =>
`Couldn't create edge for ${!sourceHandle ? 'source' : 'target'} handle id: "${
!sourceHandle ? edge.sourceHandle : edge.targetHandle
}", edge id: ${edge.id}.`,
'010': () => 'Handle: No node id found. Make sure to only use a Handle inside a custom Node.',
'011': (edgeType: string) => `Edge type "${edgeType}" not found. Using fallback type "default".`,
};
+2 -2
View File
@@ -2,13 +2,13 @@ import { useEffect, useRef, useState } from 'react';
import type { RefObject, MouseEvent } from 'react';
import { drag } from 'd3-drag';
import { select } from 'd3-selection';
import { calcAutoPan, getEventPosition } from '@reactflow/utils';
import type { NodeDragItem, Node, SelectionDragHandler, UseDragEvent, XYPosition } from '@reactflow/system';
import { useStoreApi } from '../../hooks/useStore';
import { getDragItems, getEventHandlerParams, hasSelector, calcNextPosition } from './utils';
import { handleNodeClick } from '../../components/Nodes/utils';
import useGetPointerPosition from '../useGetPointerPosition';
import { calcAutoPan, getEventPosition } from '../../utils';
import type { NodeDragItem, Node, SelectionDragHandler, UseDragEvent, XYPosition } from '../../types';
export type UseDragData = { dx: number; dy: number };
+11 -5
View File
@@ -1,9 +1,15 @@
import type { RefObject } from 'react';
import { clampPosition, isNumeric } from '../../utils';
import type { CoordinateExtent, Node, NodeDragItem, NodeInternals, NodeOrigin, OnError, XYPosition } from '../../types';
import { getNodePositionWithOrigin } from '../../utils/graph';
import { errorMessages } from '../../contants';
import {
errorMessages,
type CoordinateExtent,
type Node,
type NodeDragItem,
type NodeInternals,
type NodeOrigin,
type OnError,
type XYPosition,
} from '@reactflow/system';
import { clampPosition, isNumeric, getNodePositionWithOrigin } from '@reactflow/utils';
export function isParentSelected(node: Node, nodeInternals: NodeInternals): boolean {
if (!node.parentNode) {
+2 -1
View File
@@ -1,5 +1,6 @@
import type { Edge, ReactFlowState } from '@reactflow/system';
import { useStore } from '../hooks/useStore';
import type { Edge, ReactFlowState } from '../types';
const edgesSelector = (state: ReactFlowState) => state.edges;
@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import type { UseDragEvent } from '@reactflow/system';
import { useStoreApi } from './useStore';
import type { UseDragEvent } from '../types';
function useGetPointerPosition() {
const store = useStoreApi();
@@ -1,8 +1,8 @@
import { useEffect } from 'react';
import type { KeyCode } from '@reactflow/system';
import { useStoreApi } from '../hooks/useStore';
import useKeyPress from './useKeyPress';
import type { KeyCode } from '../types';
import useReactFlow from './useReactFlow';
interface HookParams {
deleteKeyCode: KeyCode | null;
+2 -3
View File
@@ -1,7 +1,6 @@
import { useState, useEffect, useRef, useMemo } from 'react';
import { isInputDOMNode } from '../utils';
import type { KeyCode } from '../types';
import { isInputDOMNode } from '@reactflow/utils';
import type { KeyCode } from '@reactflow/system';
type Keys = Array<string>;
type PressedKeys = Set<string>;
+1 -1
View File
@@ -1,5 +1,5 @@
import { useStore } from '../hooks/useStore';
import type { Node, ReactFlowState } from '../types';
import type { Node, ReactFlowState } from '@reactflow/system';
const nodesSelector = (state: ReactFlowState) => state.getNodes();
@@ -1,9 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useState, useCallback } from 'react';
import type { SetStateAction, 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 type { Node, NodeChange, Edge, EdgeChange } from '../types';
type ApplyChanges<ItemType, ChangesType> = (changes: ChangesType[], items: ItemType[]) => ItemType[];
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
@@ -1,7 +1,6 @@
import { internalsSymbol } from '@reactflow/system';
import { internalsSymbol, type ReactFlowState } from '@reactflow/system';
import { useStore } from './useStore';
import type { ReactFlowState } from '../types';
const selector = (s: ReactFlowState) => {
if (s.nodeInternals.size === 0) {
+1 -1
View File
@@ -1,7 +1,7 @@
import { useEffect, useRef } from 'react';
import type { OnInit } from '@reactflow/system';
import useReactFlow from './useReactFlow';
import type { OnInit } from '../types';
function useOnInitHandler(onInit: OnInit | undefined) {
const rfInstance = useReactFlow();
@@ -1,7 +1,7 @@
import { useEffect } from 'react';
import type { OnSelectionChangeFunc } from '@reactflow/system';
import { useStoreApi } from './useStore';
import type { OnSelectionChangeFunc } from '../types';
export type UseOnSelectionChangeOptions = {
onChange?: OnSelectionChangeFunc;
@@ -1,7 +1,7 @@
import { useEffect } from 'react';
import type { OnViewportChange } from '@reactflow/system';
import { useStoreApi } from './useStore';
import type { OnViewportChange } from '../types';
export type UseOnViewportChangeOptions = {
onStart?: OnViewportChange;
+5 -6
View File
@@ -1,7 +1,5 @@
import { useCallback, useMemo } from 'react';
import useViewportHelper from './useViewportHelper';
import { useStoreApi } from '../hooks/useStore';
import { getConnectedEdges, getOverlappingArea, isRectObject, nodeToRect } from '@reactflow/utils';
import type {
ReactFlowInstance,
Instance,
@@ -14,9 +12,10 @@ import type {
NodeChange,
Node,
Rect,
} from '../types';
import { getConnectedEdges } from '../utils/graph';
import { getOverlappingArea, isRectObject, nodeToRect } from '../utils';
} from '@reactflow/system';
import useViewportHelper from './useViewportHelper';
import { useStoreApi } from '../hooks/useStore';
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
+3 -4
View File
@@ -1,9 +1,8 @@
import { useEffect } from 'react';
import type { MutableRefObject } from 'react';
import { useEffect, type MutableRefObject } from 'react';
import { errorMessages } from '@reactflow/system';
import { getDimensions } from '@reactflow/utils';
import { useStoreApi } from '../hooks/useStore';
import { getDimensions } from '../utils';
import { errorMessages } from '../contants';
function useResizeHandler(rendererNode: MutableRefObject<HTMLDivElement | null>): void {
const store = useStoreApi();
+2 -4
View File
@@ -1,10 +1,8 @@
import { useContext, useMemo } from 'react';
import { useStore as useZustandStore } from 'zustand';
import type { StoreApi } from 'zustand';
import { useStore as useZustandStore, type StoreApi } from 'zustand';
import { errorMessages, type ReactFlowState } from '@reactflow/system';
import StoreContext from '../contexts/RFStoreContext';
import { errorMessages } from '../contants';
import type { ReactFlowState } from '../types';
const zustandErrorMessage = errorMessages['001']();
@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import type { UpdateNodeInternals } from '@reactflow/system';
import { useStoreApi } from '../hooks/useStore';
import type { UpdateNodeInternals } from '../types';
function useUpdateNodeInternals(): UpdateNodeInternals {
const store = useStoreApi();
+1 -1
View File
@@ -1,7 +1,7 @@
import { shallow } from 'zustand/shallow';
import type { Viewport, ReactFlowState } from '@reactflow/system';
import { useStore } from '../hooks/useStore';
import type { Viewport, ReactFlowState } from '../types';
const viewportSelector = (state: ReactFlowState) => ({
x: state.transform[0],
+2 -2
View File
@@ -1,11 +1,11 @@
import { useMemo } from 'react';
import { zoomIdentity } from 'd3-zoom';
import { shallow } from 'zustand/shallow';
import { pointToRendererPoint, getTransformForBounds, getD3Transition } from '@reactflow/utils';
import type { ViewportHelperFunctions, ReactFlowState, XYPosition } from '@reactflow/system';
import { useStoreApi, useStore } from '../hooks/useStore';
import { pointToRendererPoint, getTransformForBounds, getD3Transition } from '../utils/graph';
import { fitView } from '../store/utils';
import type { ViewportHelperFunctions, ReactFlowState, XYPosition } from '../types';
// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};
+2 -3
View File
@@ -1,10 +1,9 @@
import { useCallback } from 'react';
import { internalsSymbol } from '@reactflow/system';
import { internalsSymbol, type ReactFlowState, type NodeInternals, type Edge } from '@reactflow/system';
import { isNumeric } from '@reactflow/utils';
import { useStore } from '../hooks/useStore';
import { isEdgeVisible } from '../container/EdgeRenderer/utils';
import { isNumeric } from '../utils';
import type { ReactFlowState, NodeInternals, Edge } from '../types';
const defaultEdgeTree = [{ level: 0, isMaxLevel: true, edges: [] }];
+2 -2
View File
@@ -1,8 +1,8 @@
import { useCallback } from 'react';
import { getNodesInside } from '@reactflow/utils';
import type { ReactFlowState } from '@reactflow/system';
import { useStore } from '../hooks/useStore';
import { getNodesInside } from '../utils/graph';
import type { ReactFlowState } from '../types';
function useVisibleNodes(onlyRenderVisible: boolean) {
const nodes = useStore(
+4 -2
View File
@@ -8,7 +8,6 @@ export { default as SimpleBezierEdge, getSimpleBezierPath } from './components/E
export { default as SmoothStepEdge, getSmoothStepPath } from './components/Edges/SmoothStepEdge';
export { default as BaseEdge } from './components/Edges/BaseEdge';
export { rectToBox, boxToRect, getBoundsOfRects } from './utils';
export {
isNode,
isEdge,
@@ -20,7 +19,10 @@ export {
getTransformForBounds,
getRectOfNodes,
getNodePositionWithOrigin,
} from './utils/graph';
rectToBox,
boxToRect,
getBoundsOfRects,
} from '@reactflow/utils';
export { applyNodeChanges, applyEdgeChanges } from './utils/changes';
export { getMarkerEnd } from './components/Edges/utils';
export { default as ReactFlowProvider } from './components/ReactFlowProvider';
+17 -17
View File
@@ -1,27 +1,27 @@
import { createStore } from 'zustand';
import { zoomIdentity } from 'd3-zoom';
import { internalsSymbol } from '@reactflow/system';
import { clampPosition, getDimensions } from '@reactflow/utils';
import {
internalsSymbol,
type ReactFlowState,
type Node,
type Edge,
type NodeDimensionUpdate,
type CoordinateExtent,
type NodeDimensionChange,
type EdgeSelectionChange,
type NodeSelectionChange,
type NodePositionChange,
type NodeDragItem,
type UnselectNodesAndEdgesParams,
type NodeChange,
type XYPosition,
} from '@reactflow/system';
import { clampPosition, getDimensions } from '../utils';
import { applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
import { getHandleBounds } from '../components/Nodes/utils';
import { createNodeInternals, fitView, updateAbsoluteNodePositions, updateNodesAndEdgesSelections } from './utils';
import initialState from './initialState';
import type {
ReactFlowState,
Node,
Edge,
NodeDimensionUpdate,
CoordinateExtent,
NodeDimensionChange,
EdgeSelectionChange,
NodeSelectionChange,
NodePositionChange,
NodeDragItem,
UnselectNodesAndEdgesParams,
NodeChange,
XYPosition,
} from '../types';
const createRFStore = () =>
createStore<ReactFlowState>((set, get) => ({
+2 -3
View File
@@ -1,6 +1,5 @@
import { devWarn } from '../utils';
import { ConnectionMode } from '../types';
import type { CoordinateExtent, ReactFlowStore } from '../types';
import { devWarn } from '@reactflow/utils';
import { ConnectionMode, type CoordinateExtent, type ReactFlowStore } from '@reactflow/system';
export const infiniteExtent: CoordinateExtent = [
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
+19 -15
View File
@@ -1,20 +1,24 @@
import { zoomIdentity } from 'd3-zoom';
import type { StoreApi } from 'zustand';
import { internalsSymbol } from '@reactflow/system';
import { isNumeric } from '../utils';
import { getD3Transition, getRectOfNodes, getTransformForBounds, getNodePositionWithOrigin } from '../utils/graph';
import type {
Edge,
EdgeSelectionChange,
Node,
NodeInternals,
NodeSelectionChange,
ReactFlowState,
XYZPosition,
FitViewOptions,
NodeOrigin,
} from '../types';
import {
internalsSymbol,
type Edge,
type EdgeSelectionChange,
type Node,
type NodeInternals,
type NodeSelectionChange,
type ReactFlowState,
type XYZPosition,
type FitViewOptions,
type NodeOrigin,
} from '@reactflow/system';
import {
isNumeric,
getD3Transition,
getRectOfNodes,
getTransformForBounds,
getNodePositionWithOrigin,
} from '@reactflow/utils';
type ParentNodes = Record<string, boolean>;
-62
View File
@@ -1,62 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { XYPosition, Dimensions } from './utils';
import type { Node } from './nodes';
import type { Edge } from './edges';
export type NodeDimensionChange = {
id: string;
type: 'dimensions';
dimensions?: Dimensions;
updateStyle?: boolean;
resizing?: boolean;
};
export type NodePositionChange = {
id: string;
type: 'position';
position?: XYPosition;
positionAbsolute?: XYPosition;
dragging?: boolean;
};
export type NodeSelectionChange = {
id: string;
type: 'select';
selected: boolean;
};
export type NodeRemoveChange = {
id: string;
type: 'remove';
};
export type NodeAddChange<NodeData = any> = {
item: Node<NodeData>;
type: 'add';
};
export type NodeResetChange<NodeData = any> = {
item: Node<NodeData>;
type: 'reset';
};
export type NodeChange =
| NodeDimensionChange
| NodePositionChange
| NodeSelectionChange
| NodeRemoveChange
| NodeAddChange
| NodeResetChange;
export type EdgeSelectionChange = NodeSelectionChange;
export type EdgeRemoveChange = NodeRemoveChange;
export type EdgeAddChange<EdgeData = any> = {
item: Edge<EdgeData>;
type: 'add';
};
export type EdgeResetChange<EdgeData = any> = {
item: Edge<EdgeData>;
type: 'reset';
};
export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange | EdgeAddChange | EdgeResetChange;
-149
View File
@@ -1,149 +0,0 @@
import type { CSSProperties, HTMLAttributes, MouseEvent as ReactMouseEvent, WheelEvent } from 'react';
import type {
OnSelectionChangeFunc,
NodeTypes,
EdgeTypes,
Node,
Edge,
ConnectionMode,
ConnectionLineType,
ConnectionLineComponent,
OnConnectStart,
OnConnectEnd,
OnConnect,
CoordinateExtent,
KeyCode,
PanOnScrollMode,
OnEdgeUpdateFunc,
OnInit,
ProOptions,
PanelPosition,
DefaultEdgeOptions,
FitViewOptions,
OnNodesDelete,
OnEdgesDelete,
OnNodesChange,
OnEdgesChange,
OnMove,
OnMoveStart,
OnMoveEnd,
NodeDragHandler,
NodeMouseHandler,
SelectionDragHandler,
Viewport,
NodeOrigin,
EdgeMouseHandler,
HandleType,
SelectionMode,
OnError,
} from '.';
export type ReactFlowProps = HTMLAttributes<HTMLDivElement> & {
nodes?: Node[];
edges?: Edge[];
defaultNodes?: Node[];
defaultEdges?: Edge[];
defaultEdgeOptions?: DefaultEdgeOptions;
onNodeClick?: NodeMouseHandler;
onNodeDoubleClick?: NodeMouseHandler;
onNodeMouseEnter?: NodeMouseHandler;
onNodeMouseMove?: NodeMouseHandler;
onNodeMouseLeave?: NodeMouseHandler;
onNodeContextMenu?: NodeMouseHandler;
onNodeDragStart?: NodeDragHandler;
onNodeDrag?: NodeDragHandler;
onNodeDragStop?: NodeDragHandler;
onEdgeClick?: (event: ReactMouseEvent, node: Edge) => void;
onEdgeUpdate?: OnEdgeUpdateFunc;
onEdgeContextMenu?: EdgeMouseHandler;
onEdgeMouseEnter?: EdgeMouseHandler;
onEdgeMouseMove?: EdgeMouseHandler;
onEdgeMouseLeave?: EdgeMouseHandler;
onEdgeDoubleClick?: EdgeMouseHandler;
onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge, handleType: HandleType) => void;
onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent, edge: Edge, handleType: HandleType) => void;
onNodesChange?: OnNodesChange;
onEdgesChange?: OnEdgesChange;
onNodesDelete?: OnNodesDelete;
onEdgesDelete?: OnEdgesDelete;
onSelectionDragStart?: SelectionDragHandler;
onSelectionDrag?: SelectionDragHandler;
onSelectionDragStop?: SelectionDragHandler;
onSelectionStart?: (event: ReactMouseEvent) => void;
onSelectionEnd?: (event: ReactMouseEvent) => void;
onSelectionContextMenu?: (event: ReactMouseEvent, nodes: Node[]) => void;
onConnect?: OnConnect;
onConnectStart?: OnConnectStart;
onConnectEnd?: OnConnectEnd;
onClickConnectStart?: OnConnectStart;
onClickConnectEnd?: OnConnectEnd;
onInit?: OnInit;
onMove?: OnMove;
onMoveStart?: OnMoveStart;
onMoveEnd?: OnMoveEnd;
onSelectionChange?: OnSelectionChangeFunc;
onPaneScroll?: (event?: WheelEvent) => void;
onPaneClick?: (event: ReactMouseEvent) => void;
onPaneContextMenu?: (event: ReactMouseEvent) => void;
onPaneMouseEnter?: (event: ReactMouseEvent) => void;
onPaneMouseMove?: (event: ReactMouseEvent) => void;
onPaneMouseLeave?: (event: ReactMouseEvent) => void;
nodeTypes?: NodeTypes;
edgeTypes?: EdgeTypes;
connectionLineType?: ConnectionLineType;
connectionLineStyle?: CSSProperties;
connectionLineComponent?: ConnectionLineComponent;
connectionLineContainerStyle?: CSSProperties;
connectionMode?: ConnectionMode;
deleteKeyCode?: KeyCode | null;
selectionKeyCode?: KeyCode | null;
selectionOnDrag?: boolean;
selectionMode?: SelectionMode;
panActivationKeyCode?: KeyCode | null;
multiSelectionKeyCode?: KeyCode | null;
zoomActivationKeyCode?: KeyCode | null;
snapToGrid?: boolean;
snapGrid?: [number, number];
onlyRenderVisibleElements?: boolean;
nodesDraggable?: boolean;
nodesConnectable?: boolean;
nodesFocusable?: boolean;
nodeOrigin?: NodeOrigin;
edgesFocusable?: boolean;
initNodeOrigin?: NodeOrigin;
elementsSelectable?: boolean;
selectNodesOnDrag?: boolean;
panOnDrag?: boolean | number[];
minZoom?: number;
maxZoom?: number;
defaultViewport?: Viewport;
translateExtent?: CoordinateExtent;
preventScrolling?: boolean;
nodeExtent?: CoordinateExtent;
defaultMarkerColor?: string;
zoomOnScroll?: boolean;
zoomOnPinch?: boolean;
panOnScroll?: boolean;
panOnScrollSpeed?: number;
panOnScrollMode?: PanOnScrollMode;
zoomOnDoubleClick?: boolean;
edgeUpdaterRadius?: number;
noDragClassName?: string;
noWheelClassName?: string;
noPanClassName?: string;
fitView?: boolean;
fitViewOptions?: FitViewOptions;
connectOnClick?: boolean;
attributionPosition?: PanelPosition;
proOptions?: ProOptions;
elevateNodesOnSelect?: boolean;
elevateEdgesOnSelect?: boolean;
disableKeyboardA11y?: boolean;
autoPanOnNodeDrag?: boolean;
autoPanOnConnect?: boolean;
connectionRadius?: number;
onError?: OnError;
};
export type ReactFlowRefType = HTMLDivElement;
-180
View File
@@ -1,180 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { CSSProperties, ComponentType, HTMLAttributes, ReactNode, MouseEvent as ReactMouseEvent } from 'react';
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;
type?: string;
source: string;
target: string;
sourceHandle?: string | null;
targetHandle?: string | null;
style?: CSSProperties;
animated?: boolean;
hidden?: boolean;
deletable?: boolean;
data?: T;
className?: string;
sourceNode?: Node;
targetNode?: Node;
selected?: boolean;
markerStart?: EdgeMarkerType;
markerEnd?: EdgeMarkerType;
zIndex?: number;
ariaLabel?: string;
interactionWidth?: number;
focusable?: boolean;
} & EdgeLabelOptions;
export type SmoothStepPathOptions = {
offset?: number;
borderRadius?: number;
};
type SmoothStepEdgeType<T> = DefaultEdge<T> & {
type: 'smoothstep';
pathOptions?: SmoothStepPathOptions;
};
export type BezierPathOptions = {
curvature?: number;
};
type BezierEdgeType<T> = DefaultEdge<T> & {
type: 'default';
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'
>;
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 {
Bezier = 'default',
Straight = 'straight',
Step = 'step',
SmoothStep = 'smoothstep',
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 = {
type: MarkerType;
color?: string;
width?: number;
height?: number;
markerUnits?: string;
orient?: string;
strokeWidth?: number;
};
export type EdgeMarkerType = string | EdgeMarker;
export enum MarkerType {
Arrow = 'arrow',
ArrowClosed = 'arrowclosed',
}
-279
View File
@@ -1,279 +0,0 @@
/* 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 { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
import type { NodeChange, EdgeChange } from './changes';
import type {
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 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 OnMoveStart = OnMove;
export type OnMoveEnd = OnMove;
export type ZoomInOut = (options?: ViewportHelperFunctionOptions) => void;
export type ZoomTo = (zoomLevel: number, options?: ViewportHelperFunctionOptions) => void;
export type GetZoom = () => number;
export type GetViewport = () => Viewport;
export type SetViewport = (viewport: Viewport, options?: ViewportHelperFunctionOptions) => void;
export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => void;
export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => void;
export type OnInit<NodeData = any, EdgeData = any> = (reactFlowInstance: ReactFlowInstance<NodeData, EdgeData>) => void;
export interface Connection {
source: string | null;
target: string | null;
sourceHandle: string | null;
targetHandle: string | null;
}
export type ConnectionStatus = 'valid' | 'invalid';
export enum ConnectionMode {
Strict = 'strict',
Loose = 'loose',
}
export type OnConnect = (connection: Connection) => void;
export type FitViewOptions = {
padding?: number;
includeHiddenNodes?: boolean;
minZoom?: number;
maxZoom?: number;
duration?: number;
nodes?: (Partial<Node> & { id: Node['id'] })[];
};
export type OnConnectStartParams = {
nodeId: string | null;
handleId: string | null;
handleType: HandleType | null;
};
export type OnConnectStart = (event: ReactMouseEvent | ReactTouchEvent, params: OnConnectStartParams) => void;
export type OnConnectEnd = (event: MouseEvent | TouchEvent) => void;
export type Viewport = {
x: number;
y: number;
zoom: number;
};
export type KeyCode = string | Array<string>;
export type SnapGrid = [number, number];
export enum PanOnScrollMode {
Free = 'free',
Vertical = 'vertical',
Horizontal = 'horizontal',
}
export type ViewportHelperFunctionOptions = {
duration?: number;
};
export type SetCenterOptions = ViewportHelperFunctionOptions & {
zoom?: number;
};
export type FitBoundsOptions = ViewportHelperFunctionOptions & {
padding?: number;
};
export type UnselectNodesAndEdgesParams = {
nodes?: Node[];
edges?: Edge[];
};
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 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: ZoomBehavior<Element, unknown> | null;
d3Selection: D3Selection<Element, unknown, null, undefined> | 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 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 ProOptions = {
account?: string;
hideAttribution: boolean;
};
export type UseDragEvent = D3DragEvent<HTMLDivElement, null, SubjectPosition>;
export enum SelectionMode {
Partial = 'partial',
Full = 'full',
}
export type SelectionRect = Rect & {
startX: number;
startY: number;
};
export type OnError = (id: string, message: string) => void;
-23
View File
@@ -1,23 +0,0 @@
import type { XYPosition, Position, Dimensions, OnConnect, Connection } from '.';
export type HandleType = 'source' | 'target';
export interface HandleElement extends XYPosition, Dimensions {
id?: string | null;
position: Position;
}
export interface StartHandle {
nodeId: string;
type: HandleType;
handleId?: string | null;
}
export interface HandleProps {
type: HandleType;
position: Position;
isConnectable?: boolean;
onConnect?: OnConnect;
isValidConnection?: (connection: Connection) => boolean;
id?: string;
}
-8
View File
@@ -1,8 +0,0 @@
export * from './general';
export * from './nodes';
export * from './edges';
export * from './handles';
export * from './changes';
export * from './utils';
export * from './instance';
export * from './component-props';
-56
View File
@@ -1,56 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-namespace */
import { ViewportHelperFunctions, Viewport, Node, Edge, Rect } from '.';
export type ReactFlowJsonObject<NodeData = any, EdgeData = any> = {
nodes: Node<NodeData>[];
edges: Edge<EdgeData>[];
viewport: Viewport;
};
export type DeleteElementsOptions = {
nodes?: (Partial<Node> & { id: Node['id'] })[];
edges?: (Partial<Edge> & { id: Edge['id'] })[];
};
export namespace Instance {
export type GetNodes<NodeData> = () => Node<NodeData>[];
export type SetNodes<NodeData> = (
payload: Node<NodeData>[] | ((nodes: Node<NodeData>[]) => Node<NodeData>[])
) => void;
export type AddNodes<NodeData> = (payload: Node<NodeData>[] | Node<NodeData>) => void;
export type GetNode<NodeData> = (id: string) => Node<NodeData> | undefined;
export type GetEdges<EdgeData> = () => Edge<EdgeData>[];
export type SetEdges<EdgeData> = (
payload: Edge<EdgeData>[] | ((edges: Edge<EdgeData>[]) => Edge<EdgeData>[])
) => void;
export type GetEdge<EdgeData> = (id: string) => Edge<EdgeData> | undefined;
export type AddEdges<EdgeData> = (payload: Edge<EdgeData>[] | Edge<EdgeData>) => void;
export type ToObject<NodeData = any, EdgeData = any> = () => ReactFlowJsonObject<NodeData, EdgeData>;
export type DeleteElements = ({ nodes, edges }: DeleteElementsOptions) => void;
export type GetIntersectingNodes<NodeData> = (
node: (Partial<Node<NodeData>> & { id: Node['id'] }) | Rect,
partially?: boolean,
nodes?: Node<NodeData>[]
) => Node<NodeData>[];
export type IsNodeIntersecting<NodeData> = (
node: (Partial<Node<NodeData>> & { id: Node['id'] }) | Rect,
area: Rect,
partially?: boolean
) => boolean;
}
export type ReactFlowInstance<NodeData = any, EdgeData = any> = {
getNodes: Instance.GetNodes<NodeData>;
setNodes: Instance.SetNodes<NodeData>;
addNodes: Instance.AddNodes<NodeData>;
getNode: Instance.GetNode<NodeData>;
getEdges: Instance.GetEdges<EdgeData>;
setEdges: Instance.SetEdges<EdgeData>;
addEdges: Instance.AddEdges<EdgeData>;
getEdge: Instance.GetEdge<EdgeData>;
toObject: Instance.ToObject<NodeData, EdgeData>;
deleteElements: Instance.DeleteElements;
getIntersectingNodes: Instance.GetIntersectingNodes<NodeData>;
isNodeIntersecting: Instance.IsNodeIntersecting<NodeData>;
viewportInitialized: boolean;
} & Omit<ViewportHelperFunctions, 'initialized'>;
-118
View File
@@ -1,118 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
import { internalsSymbol } from '@reactflow/system';
import type { XYPosition, Position, CoordinateExtent, HandleElement } from '.';
// interface for the user node items
export type Node<T = any, U extends string | undefined = string | undefined> = {
id: string;
position: XYPosition;
data: T;
type?: U;
style?: CSSProperties;
className?: string;
sourcePosition?: Position;
targetPosition?: Position;
hidden?: boolean;
selected?: boolean;
dragging?: boolean;
draggable?: boolean;
selectable?: boolean;
connectable?: boolean;
deletable?: boolean;
dragHandle?: string;
width?: number | null;
height?: number | null;
parentNode?: string;
zIndex?: number;
extent?: 'parent' | CoordinateExtent;
expandParent?: boolean;
positionAbsolute?: XYPosition;
ariaLabel?: string;
focusable?: boolean;
resizing?: boolean;
// only used internally
[internalsSymbol]?: {
z?: number;
handleBounds?: NodeHandleBounds;
isParent?: 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<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
export type NodeProps<T = any> = Pick<
WrapNodeProps<T>,
'id' | 'data' | 'dragHandle' | 'type' | 'selected' | 'isConnectable' | 'xPos' | 'yPos' | 'zIndex'
> & {
dragging: boolean;
targetPosition?: Position;
sourcePosition?: Position;
};
export type NodeHandleBounds = {
source: HandleElement[] | null;
target: HandleElement[] | null;
};
export type NodeDimensionUpdate = {
id: string;
nodeElement: HTMLDivElement;
forceUpdate?: boolean;
};
export type NodeInternals = Map<string, Node>;
export type NodeBounds = XYPosition & {
width: number | null;
height: number | null;
};
export type NodeDragItem = {
id: string;
position: XYPosition;
positionAbsolute: XYPosition;
// distance from the mouse cursor to the node when start dragging
distance: XYPosition;
width?: number | null;
height?: number | null;
extent?: 'parent' | CoordinateExtent;
parentNode?: string;
dragging?: boolean;
};
export type NodeOrigin = [number, number];
-29
View File
@@ -1,29 +0,0 @@
export enum Position {
Left = 'left',
Top = 'top',
Right = 'right',
Bottom = 'bottom',
}
export interface XYPosition {
x: number;
y: number;
}
export type XYZPosition = XYPosition & { z: number };
export interface Dimensions {
width: number;
height: number;
}
export interface Rect extends Dimensions, XYPosition {}
export interface Box extends XYPosition {
x2: number;
y2: number;
}
export type Transform = [number, number, number];
export type CoordinateExtent = [[number, number], [number, number]];
+1 -1
View File
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Node, Edge, EdgeChange, NodeChange } from '../types';
import type { Node, Edge, EdgeChange, NodeChange } from '@reactflow/system';
function handleParentExpand(res: any[], updateItem: any) {
const parent = res.find((e) => e.id === updateItem.parentNode);
-287
View File
@@ -1,287 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Selection as D3Selection } from 'd3';
import { boxToRect, clamp, devWarn, getBoundsOfBoxes, getOverlappingArea, rectToBox } from '../utils';
import {
Node,
Edge,
Connection,
EdgeMarkerType,
Transform,
XYPosition,
Rect,
NodeInternals,
NodeOrigin,
} from '../types';
import { errorMessages } from '../contants';
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
'id' in element && 'source' in element && 'target' in element;
export const isNode = (element: Node | Connection | Edge): element is Node =>
'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>[] => {
if (!isNode(node)) {
return [];
}
const outgoerIds = edges.filter((e) => e.source === node.id).map((e) => e.target);
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>[] => {
if (!isNode(node)) {
return [];
}
const incomersIds = edges.filter((e) => e.target === node.id).map((e) => e.source);
return nodes.filter((n) => incomersIds.includes(n.id));
};
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection): string =>
`reactflow__edge-${source}${sourceHandle || ''}-${target}${targetHandle || ''}`;
export const getMarkerId = (marker: EdgeMarkerType | undefined, rfId?: string): string => {
if (typeof marker === 'undefined') {
return '';
}
if (typeof marker === 'string') {
return marker;
}
const idPrefix = rfId ? `${rfId}__` : '';
return `${idPrefix}${Object.keys(marker)
.sort()
.map((key: string) => `${key}=${(marker as any)[key]}`)
.join('&')}`;
};
const connectionExists = (edge: Edge, edges: Edge[]) => {
return edges.some(
(el) =>
el.source === edge.source &&
el.target === edge.target &&
(el.sourceHandle === edge.sourceHandle || (!el.sourceHandle && !edge.sourceHandle)) &&
(el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle))
);
};
export const addEdge = (edgeParams: Edge | Connection, edges: Edge[]): Edge[] => {
if (!edgeParams.source || !edgeParams.target) {
devWarn('006', errorMessages['006']());
return edges;
}
let edge: Edge;
if (isEdge(edgeParams)) {
edge = { ...edgeParams };
} else {
edge = {
...edgeParams,
id: getEdgeId(edgeParams),
} as Edge;
}
if (connectionExists(edge, edges)) {
return edges;
}
return edges.concat(edge);
};
export const updateEdge = (oldEdge: Edge, newConnection: Connection, edges: Edge[]): Edge[] => {
if (!newConnection.source || !newConnection.target) {
devWarn('006', errorMessages['006']());
return edges;
}
const foundEdge = edges.find((e) => e.id === oldEdge.id) as Edge;
if (!foundEdge) {
devWarn('007', errorMessages['007'](oldEdge.id));
return edges;
}
// Remove old edge and create the new edge with parameters of old edge.
const edge = {
...oldEdge,
id: getEdgeId(newConnection),
source: newConnection.source,
target: newConnection.target,
sourceHandle: newConnection.sourceHandle,
targetHandle: newConnection.targetHandle,
} as Edge;
return edges.filter((e) => e.id !== oldEdge.id).concat(edge);
};
export const pointToRendererPoint = (
{ x, y }: XYPosition,
[tx, ty, tScale]: Transform,
snapToGrid: boolean,
[snapX, snapY]: [number, number]
): XYPosition => {
const position: XYPosition = {
x: (x - tx) / tScale,
y: (y - ty) / tScale,
};
if (snapToGrid) {
return {
x: snapX * Math.round(position.x / snapX),
y: snapY * Math.round(position.y / snapY),
};
}
return position;
};
export const rendererPointToPoint = ({ x, y }: XYPosition, [tx, ty, tScale]: Transform): XYPosition => {
return {
x: x * tScale + tx,
y: y * tScale + ty,
};
};
export const getNodePositionWithOrigin = (
node: Node | undefined,
nodeOrigin: NodeOrigin = [0, 0]
): XYPosition & { positionAbsolute: XYPosition } => {
if (!node) {
return {
x: 0,
y: 0,
positionAbsolute: {
x: 0,
y: 0,
},
};
}
const offsetX = (node.width ?? 0) * nodeOrigin[0];
const offsetY = (node.height ?? 0) * nodeOrigin[1];
const position: XYPosition = {
x: node.position.x - offsetX,
y: node.position.y - offsetY,
};
return {
...position,
positionAbsolute: node.positionAbsolute
? {
x: node.positionAbsolute.x - offsetX,
y: node.positionAbsolute.y - offsetY,
}
: position,
};
};
export const getRectOfNodes = (nodes: Node[], nodeOrigin: NodeOrigin = [0, 0]): Rect => {
if (nodes.length === 0) {
return { x: 0, y: 0, width: 0, height: 0 };
}
const box = nodes.reduce(
(currBox, node) => {
const { x, y } = getNodePositionWithOrigin(node, nodeOrigin).positionAbsolute;
return getBoundsOfBoxes(
currBox,
rectToBox({
x,
y,
width: node.width || 0,
height: node.height || 0,
})
);
},
{ x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity }
);
return boxToRect(box);
};
export const getNodesInside = (
nodeInternals: NodeInternals,
rect: Rect,
[tx, ty, tScale]: Transform = [0, 0, 1],
partially = false,
// set excludeNonSelectableNodes if you want to pay attention to the nodes "selectable" attribute
excludeNonSelectableNodes = false,
nodeOrigin: NodeOrigin = [0, 0]
): Node[] => {
const paneRect = {
x: (rect.x - tx) / tScale,
y: (rect.y - ty) / tScale,
width: rect.width / tScale,
height: rect.height / tScale,
};
const visibleNodes: Node[] = [];
nodeInternals.forEach((node) => {
const { width, height, selectable = true, hidden = false } = node;
if ((excludeNonSelectableNodes && !selectable) || hidden) {
return false;
}
const { positionAbsolute } = getNodePositionWithOrigin(node, nodeOrigin);
const nodeRect = {
x: positionAbsolute.x,
y: positionAbsolute.y,
width: width || 0,
height: height || 0,
};
const overlappingArea = getOverlappingArea(paneRect, nodeRect);
const notInitialized =
typeof width === 'undefined' || typeof height === 'undefined' || width === null || height === null;
const partiallyVisible = partially && overlappingArea > 0;
const area = (width || 0) * (height || 0);
const isVisible = notInitialized || partiallyVisible || overlappingArea >= area;
if (isVisible || node.dragging) {
visibleNodes.push(node);
}
});
return visibleNodes;
};
export const getConnectedEdges = (nodes: Node[], edges: Edge[]): Edge[] => {
const nodeIds = nodes.map((node) => node.id);
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target));
};
export const getTransformForBounds = (
bounds: Rect,
width: number,
height: number,
minZoom: number,
maxZoom: number,
padding = 0.1
): Transform => {
const xZoom = width / (bounds.width * (1 + padding));
const yZoom = height / (bounds.height * (1 + padding));
const zoom = Math.min(xZoom, yZoom);
const clampedZoom = clamp(zoom, minZoom, maxZoom);
const boundsCenterX = bounds.x + bounds.width / 2;
const boundsCenterY = bounds.y + bounds.height / 2;
const x = width / 2 - boundsCenterX * clampedZoom;
const y = height / 2 - boundsCenterY * clampedZoom;
return [x, y, clampedZoom];
};
export const getD3Transition = (selection: D3Selection<Element, unknown, null, undefined>, duration = 0) => {
return selection.transition().duration(duration);
};
-128
View File
@@ -1,128 +0,0 @@
import type {
KeyboardEvent as ReactKeyboardEvent,
MouseEvent as ReactMouseEvent,
TouchEvent as ReactTouchEvent,
} from 'react';
import type { Dimensions, Node, XYPosition, CoordinateExtent, Box, Rect } from '../types';
export const getDimensions = (node: HTMLDivElement): Dimensions => ({
width: node.offsetWidth,
height: node.offsetHeight,
});
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]),
});
// returns a number between 0 and 1 that represents the velocity of the movement
// when the mouse is close to the edge of the canvas
const calcAutoPanVelocity = (value: number, min: number, max: number): number => {
if (value < min) {
return clamp(Math.abs(value - min), 1, 50) / 50;
} else if (value > max) {
return -clamp(Math.abs(value - max), 1, 50) / 50;
}
return 0;
};
export const calcAutoPan = (pos: XYPosition, bounds: Dimensions): number[] => {
const xMovement = calcAutoPanVelocity(pos.x, 35, bounds.width - 35) * 20;
const yMovement = calcAutoPanVelocity(pos.y, 35, bounds.height - 35) * 20;
return [xMovement, yMovement];
};
export const getHostForElement = (element: HTMLElement): Document | ShadowRoot =>
(element.getRootNode?.() as Document | ShadowRoot) || window?.document;
export const getBoundsOfBoxes = (box1: Box, box2: Box): Box => ({
x: Math.min(box1.x, box2.x),
y: Math.min(box1.y, box2.y),
x2: Math.max(box1.x2, box2.x2),
y2: Math.max(box1.y2, box2.y2),
});
export const rectToBox = ({ x, y, width, height }: Rect): Box => ({
x,
y,
x2: x + width,
y2: y + height,
});
export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
x,
y,
width: x2 - x,
height: y2 - y,
});
export const nodeToRect = (node: Node): Rect => ({
...(node.positionAbsolute || { x: 0, y: 0 }),
width: node.width || 0,
height: node.height || 0,
});
export const getBoundsOfRects = (rect1: Rect, rect2: Rect): Rect =>
boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)));
export const getOverlappingArea = (rectA: Rect, rectB: Rect): number => {
const xOverlap = Math.max(0, Math.min(rectA.x + rectA.width, rectB.x + rectB.width) - Math.max(rectA.x, rectB.x));
const yOverlap = Math.max(0, Math.min(rectA.y + rectA.height, rectB.y + rectB.height) - Math.max(rectA.y, rectB.y));
return Math.ceil(xOverlap * yOverlap);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isRectObject = (obj: any): obj is Rect =>
isNumeric(obj.width) && isNumeric(obj.height) && isNumeric(obj.x) && isNumeric(obj.y);
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
export const isNumeric = (n: any): n is number => !isNaN(n) && isFinite(n);
// used for a11y key board controls for nodes and edges
export const elementSelectionKeys = ['Enter', ' ', 'Escape'];
export const devWarn = (id: string, message: string) => {
if (process.env.NODE_ENV === 'development') {
console.warn(`[React Flow]: ${message} Help: https://reactflow.dev/error#${id}`);
}
};
const isReactKeyboardEvent = (event: KeyboardEvent | ReactKeyboardEvent): event is ReactKeyboardEvent =>
'nativeEvent' in event;
export function isInputDOMNode(event: KeyboardEvent | ReactKeyboardEvent): boolean {
const kbEvent = isReactKeyboardEvent(event) ? event.nativeEvent : event;
// using composed path for handling shadow dom
const target = (kbEvent.composedPath?.()?.[0] || event.target) as HTMLElement;
const isInput = ['INPUT', 'SELECT', 'TEXTAREA'].includes(target?.nodeName) || target?.hasAttribute('contenteditable');
// we want to be able to do a multi selection event if we are in an input field
const isModifierKey = event.ctrlKey || event.metaKey || event.shiftKey;
// when an input field is focused we don't want to trigger deletion or movement of nodes
return (isInput && !isModifierKey) || !!target?.closest('.nokey');
}
export const isMouseEvent = (
event: MouseEvent | ReactMouseEvent | TouchEvent | ReactTouchEvent
): event is MouseEvent | ReactMouseEvent => 'clientX' in event;
export const getEventPosition = (
event: MouseEvent | ReactMouseEvent | TouchEvent | ReactTouchEvent,
bounds?: DOMRect
) => {
const isMouseTriggered = isMouseEvent(event);
const evtX = isMouseTriggered ? event.clientX : event.touches?.[0].clientX;
const evtY = isMouseTriggered ? event.clientY : event.touches?.[0].clientY;
return {
x: evtX - (bounds?.left ?? 0),
y: evtY - (bounds?.top ?? 0),
};
};