chore: fix conflicts

This commit is contained in:
chang_su
2022-10-25 14:56:37 +08:00
112 changed files with 1497 additions and 693 deletions
@@ -1,6 +1,6 @@
import { CSSProperties } from 'react';
import { useStore } from '../../hooks/useStore';
import { ReactFlowState } from '../../types';
import type { ReactFlowState } from '../../types';
const style: CSSProperties = { display: 'none' };
const ariaLiveStyle: CSSProperties = {
@@ -17,25 +17,32 @@ const ariaLiveStyle: CSSProperties = {
export const ARIA_NODE_DESC_KEY = 'react-flow__node-desc';
export const ARIA_EDGE_DESC_KEY = 'react-flow__edge-desc';
export const ARIA_LIVE_MESSAGE = 'react-flow__arai-live';
export const ARIA_LIVE_MESSAGE = 'react-flow__aria-live';
const selector = (s: ReactFlowState) => s.ariaLiveMessage;
function A11yDescriptions({ rfId }: { rfId: string }) {
function AriaLiveMessage({ rfId }: { rfId: string }) {
const ariaLiveMessage = useStore(selector);
return (
<div id={`${ARIA_LIVE_MESSAGE}-${rfId}`} aria-live="assertive" aria-atomic="true" style={ariaLiveStyle}>
{ariaLiveMessage}
</div>
);
}
function A11yDescriptions({ rfId, disableKeyboardA11y }: { rfId: string; disableKeyboardA11y: boolean }) {
return (
<>
<div id={`${ARIA_NODE_DESC_KEY}-${rfId}`} style={style}>
Press enter or space to select a node. You can then use the arrow keys to move the node around, press delete to
remove it and press escape to cancel.
Press enter or space to select a node.
{!disableKeyboardA11y && 'You can then use the arrow keys to move the node around.'} Press delete to remove it
and escape to cancel.{' '}
</div>
<div id={`${ARIA_EDGE_DESC_KEY}-${rfId}`} style={style}>
Press enter or space to select an edge. You can then press delete to remove it or press escape to cancel.
</div>
<div id={`${ARIA_LIVE_MESSAGE}-${rfId}`} aria-live="assertive" aria-atomic="true" style={ariaLiveStyle}>
{ariaLiveMessage}
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
</div>
{!disableKeyboardA11y && <AriaLiveMessage rfId={rfId} />}
</>
);
}
@@ -1,5 +1,5 @@
import Panel from '../Panel';
import { PanelPosition, ProOptions } from '../../types';
import type { PanelPosition, ProOptions } from '../../types';
type AttributionProps = {
proOptions?: ProOptions;
@@ -4,9 +4,10 @@ import shallow from 'zustand/shallow';
import { useStore } from '../../hooks/useStore';
import { getBezierPath } from '../Edges/BezierEdge';
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
import { ConnectionLineType, ConnectionLineComponent, HandleType, Position, ReactFlowStore } from '../../types';
import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge';
import { internalsSymbol } from '../../utils';
import type { ConnectionLineComponent, HandleType, ReactFlowStore } from '../../types';
import { Position, ConnectionLineType } from '../../types';
type ConnectionLineProps = {
connectionNodeId: string;
@@ -1,5 +1,5 @@
import EdgeText from './EdgeText';
import { BaseEdgeProps } from '../../types';
import type { BaseEdgeProps } from '../../types';
const BaseEdge = ({
path,
@@ -26,7 +26,15 @@ const BaseEdge = ({
markerEnd={markerEnd}
markerStart={markerStart}
/>
{interactionWidth && <path d={path} fill="none" strokeOpacity={0} strokeWidth={interactionWidth} />}
{interactionWidth && (
<path
d={path}
fill="none"
strokeOpacity={0}
strokeWidth={interactionWidth}
className="react-flow__edge-interaction"
/>
)}
{label ? (
<EdgeText
x={labelX}
@@ -1,7 +1,9 @@
import { memo } from 'react';
import { BezierEdgeProps, Position } from '../../types';
import BaseEdge from './BaseEdge';
import { getBezierEdgeCenter } from './utils';
import { Position } from '../../types';
import type { BezierEdgeProps } from '../../types';
export interface GetBezierPathParams {
sourceX: number;
@@ -25,40 +27,22 @@ interface GetControlWithCurvatureParams {
function calculateControlOffset(distance: number, curvature: number): number {
if (distance >= 0) {
return 0.5 * distance;
} else {
return curvature * 25 * Math.sqrt(-distance);
}
return curvature * 25 * Math.sqrt(-distance);
}
function getControlWithCurvature({ pos, x1, y1, x2, y2, c }: GetControlWithCurvatureParams): [number, number] {
let ctX: number, ctY: number;
switch (pos) {
case Position.Left:
{
ctX = x1 - calculateControlOffset(x1 - x2, c);
ctY = y1;
}
break;
return [x1 - calculateControlOffset(x1 - x2, c), y1];
case Position.Right:
{
ctX = x1 + calculateControlOffset(x2 - x1, c);
ctY = y1;
}
break;
return [x1 + calculateControlOffset(x2 - x1, c), y1];
case Position.Top:
{
ctX = x1;
ctY = y1 - calculateControlOffset(y1 - y2, c);
}
break;
return [x1, y1 - calculateControlOffset(y1 - y2, c)];
case Position.Bottom:
{
ctX = x1;
ctY = y1 + calculateControlOffset(y2 - y1, c);
}
break;
return [x1, y1 + calculateControlOffset(y2 - y1, c)];
}
return [ctX, ctY];
}
export function getBezierPath({
@@ -69,7 +53,7 @@ export function getBezierPath({
targetY,
targetPosition = Position.Top,
curvature = 0.25,
}: GetBezierPathParams): [string, number, number, number, number] {
}: GetBezierPathParams): [path: string, labelX: number, labelY: number, offsetX: number, offsetY: number] {
const [sourceControlX, sourceControlY] = getControlWithCurvature({
pos: sourcePosition,
x1: sourceX,
@@ -86,7 +70,7 @@ export function getBezierPath({
y2: sourceY,
c: curvature,
});
const [centerX, centerY, offsetX, offsetY] = getBezierEdgeCenter({
const [labelX, labelY, offsetX, offsetY] = getBezierEdgeCenter({
sourceX,
sourceY,
targetX,
@@ -99,8 +83,8 @@ export function getBezierPath({
return [
`M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
centerX,
centerY,
labelX,
labelY,
offsetX,
offsetY,
];
@@ -1,4 +1,4 @@
import { FC, MouseEvent as ReactMouseEvent, SVGAttributes } from 'react';
import type { FC, MouseEvent as ReactMouseEvent, SVGAttributes } from 'react';
import cc from 'classcat';
import { Position } from '../../types';
@@ -1,4 +1,5 @@
import { memo, useRef, useState, useEffect, FC, PropsWithChildren } from 'react';
import { memo, useRef, useState, useEffect } from 'react';
import type { FC, PropsWithChildren } from 'react';
import cc from 'classcat';
import { EdgeTextProps, Rect } from '../../types';
@@ -41,6 +42,7 @@ const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({
<g
transform={`translate(${x - edgeTextBbox.width / 2} ${y - edgeTextBbox.height / 2})`}
className={edgeTextClasses}
visibility={edgeTextBbox.width ? 'visible' : 'hidden'}
{...rest}
>
{labelShowBg && (
@@ -1,7 +1,9 @@
import { memo } from 'react';
import { EdgeProps, Position } from '../../types';
import BaseEdge from './BaseEdge';
import { getBezierEdgeCenter } from './utils';
import { Position } from '../../types';
import type { EdgeProps } from '../../types';
export interface GetSimpleBezierPathParams {
sourceX: number;
@@ -21,24 +23,11 @@ interface GetControlParams {
}
function getControl({ pos, x1, y1, x2, y2 }: GetControlParams): [number, number] {
let ctX: number, ctY: number;
switch (pos) {
case Position.Left:
case Position.Right:
{
ctX = 0.5 * (x1 + x2);
ctY = y1;
}
break;
case Position.Top:
case Position.Bottom:
{
ctX = x1;
ctY = 0.5 * (y1 + y2);
}
break;
if (pos === Position.Left || pos === Position.Right) {
return [0.5 * (x1 + x2), y1];
}
return [ctX, ctY];
return [x1, 0.5 * (y1 + y2)];
}
export function getSimpleBezierPath({
@@ -48,7 +37,7 @@ export function getSimpleBezierPath({
targetX,
targetY,
targetPosition = Position.Top,
}: GetSimpleBezierPathParams): [string, number, number, number, number] {
}: GetSimpleBezierPathParams): [path: string, labelX: number, labelY: number, offsetX: number, offsetY: number] {
const [sourceControlX, sourceControlY] = getControl({
pos: sourcePosition,
x1: sourceX,
@@ -63,7 +52,7 @@ export function getSimpleBezierPath({
x2: sourceX,
y2: sourceY,
});
const [centerX, centerY, offsetX, offsetY] = getBezierEdgeCenter({
const [labelX, labelY, offsetX, offsetY] = getBezierEdgeCenter({
sourceX,
sourceY,
targetX,
@@ -76,8 +65,8 @@ export function getSimpleBezierPath({
return [
`M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
centerX,
centerY,
labelX,
labelY,
offsetX,
offsetY,
];
@@ -1,8 +1,9 @@
import { memo } from 'react';
import { SmoothStepEdgeProps, Position, XYPosition } from '../../types';
import BaseEdge from './BaseEdge';
import { getSimpleEdgeCenter } from './utils';
import { getEdgeCenter } from './utils';
import { Position } from '../../types';
import type { SmoothStepEdgeProps, XYPosition } from '../../types';
export interface GetSmoothStepPathParams {
sourceX: number;
@@ -72,7 +73,7 @@ function getPoints({
let points: XYPosition[] = [];
let centerX, centerY;
const [defaultCenterX, defaultCenterY, defaultOffsetX, defaultOffsetY] = getSimpleEdgeCenter({
const [defaultCenterX, defaultCenterY, defaultOffsetX, defaultOffsetY] = getEdgeCenter({
sourceX: source.x,
sourceY: source.y,
targetX: target.x,
@@ -170,7 +171,7 @@ export function getSmoothStepPath({
centerX,
centerY,
offset = 20,
}: GetSmoothStepPathParams): [string, number, number, number, number] {
}: GetSmoothStepPathParams): [path: string, labelX: number, labelY: number, offsetX: number, offsetY: number] {
const [points, labelX, labelY, offsetX, offsetY] = getPoints({
source: { x: sourceX, y: sourceY },
sourcePosition,
@@ -1,7 +1,7 @@
import { memo, useMemo } from 'react';
import { SmoothStepEdgeProps } from '../../types';
import SmoothStepEdge from './SmoothStepEdge';
import type { SmoothStepEdgeProps } from '../../types';
const StepEdge = memo((props: SmoothStepEdgeProps) => (
<SmoothStepEdge
@@ -1,8 +1,8 @@
import { memo } from 'react';
import BaseEdge from './BaseEdge';
import { EdgeProps } from '../../types';
import { getSimpleEdgeCenter } from './utils';
import { getEdgeCenter } from './utils';
import type { EdgeProps } from '../../types';
export type GetStraightPathParams = {
sourceX: number;
@@ -16,15 +16,15 @@ export function getStraightPath({
sourceY,
targetX,
targetY,
}: GetStraightPathParams): [string, number, number, number, number] {
const [centerX, centerY, offsetX, offsetY] = getSimpleEdgeCenter({
}: GetStraightPathParams): [path: string, labelX: number, labelY: number, offsetX: number, offsetY: number] {
const [labelX, labelY, offsetX, offsetY] = getEdgeCenter({
sourceX,
sourceY,
targetX,
targetY,
});
return [`M ${sourceX},${sourceY}L ${targetX},${targetY}`, centerX, centerY, offsetX, offsetY];
return [`M ${sourceX},${sourceY}L ${targetX},${targetY}`, labelX, labelY, offsetX, offsetY];
}
const StraightEdge = memo(
+2 -2
View File
@@ -1,7 +1,7 @@
import { MouseEvent as ReactMouseEvent } from 'react';
import { StoreApi } from 'zustand';
import { Edge, MarkerType, ReactFlowState } from '../../types';
import type { Edge, MarkerType, ReactFlowState } from '../../types';
export const getMarkerEnd = (markerType?: MarkerType, markerEndId?: string): string => {
if (typeof markerEndId !== 'undefined' && markerEndId) {
@@ -28,7 +28,7 @@ export function getMouseHandler(
}
// this is used for straight edges and simple smoothstep edges (LTR, RTL, BTT, TTB)
export function getSimpleEdgeCenter({
export function getEdgeCenter({
sourceX,
sourceY,
targetX,
@@ -1,4 +1,5 @@
import { memo, ComponentType, useState, useMemo, KeyboardEvent, useRef } from 'react';
import { memo, useState, useMemo, useRef } from 'react';
import type { ComponentType, KeyboardEvent } from 'react';
import cc from 'classcat';
import { useStoreApi } from '../../hooks/useStore';
@@ -7,8 +8,8 @@ import { handleMouseDown } from '../Handle/handler';
import { EdgeAnchor } from './EdgeAnchor';
import { getMarkerId } from '../../utils/graph';
import { getMouseHandler } from './utils';
import { EdgeProps, WrapEdgeProps, Connection } from '../../types';
import { elementSelectionKeys } from '../../utils';
import type { EdgeProps, WrapEdgeProps, Connection } from '../../types';
export default (EdgeComponent: ComponentType<EdgeProps>) => {
const EdgeWrapper = ({
@@ -51,7 +52,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
markerStart,
rfId,
ariaLabel,
disableKeyboardA11y,
isFocusable,
pathOptions,
interactionWidth,
}: WrapEdgeProps): JSX.Element | null => {
@@ -158,12 +159,12 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
onMouseEnter={onEdgeMouseEnter}
onMouseMove={onEdgeMouseMove}
onMouseLeave={onEdgeMouseLeave}
onKeyDown={disableKeyboardA11y ? undefined : onKeyDown}
tabIndex={disableKeyboardA11y ? undefined : 0}
role={disableKeyboardA11y ? undefined : 'button'}
onKeyDown={isFocusable ? onKeyDown : undefined}
tabIndex={isFocusable ? 0 : undefined}
role={isFocusable ? 'button' : undefined}
data-testid={`rf__edge-${id}`}
aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`}
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_EDGE_DESC_KEY}-${rfId}`}
aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined}
ref={edgeRef}
>
{!updating && (
@@ -1,8 +1,9 @@
import { MouseEvent as ReactMouseEvent } from 'react';
import type { MouseEvent as ReactMouseEvent } from 'react';
import { StoreApi } from 'zustand';
import { getHostForElement } from '../../utils';
import { OnConnect, ConnectionMode, Connection, HandleType, ReactFlowState } from '../../types';
import { ConnectionMode } from '../../types';
import type { OnConnect, Connection, HandleType, ReactFlowState } from '../../types';
type ValidConnectionFunc = (connection: Connection) => boolean;
@@ -4,10 +4,11 @@ import shallow from 'zustand/shallow';
import { useStore, useStoreApi } from '../../hooks/useStore';
import NodeIdContext from '../../contexts/NodeIdContext';
import { HandleProps, Connection, ReactFlowState, Position } from '../../types';
import { checkElementBelowIsValid, handleMouseDown } from './handler';
import { getHostForElement } from '../../utils';
import { addEdge } from '../../utils/graph';
import { Position } from '../../types';
import type { HandleProps, Connection, ReactFlowState } from '../../types';
const alwaysValid = () => true;
@@ -1,7 +1,8 @@
import { memo } from 'react';
import Handle from '../../components/Handle';
import { NodeProps, Position } from '../../types';
import { Position } from '../../types';
import type { NodeProps } from '../../types';
const DefaultNode = ({
data,
@@ -1,7 +1,8 @@
import { memo } from 'react';
import Handle from '../../components/Handle';
import { NodeProps, Position } from '../../types';
import { Position } from '../../types';
import type { NodeProps } from '../../types';
const InputNode = ({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) => (
<>
@@ -1,7 +1,8 @@
import { memo } from 'react';
import Handle from '../../components/Handle';
import { NodeProps, Position } from '../../types';
import { Position } from '../../types';
import type { NodeProps } from '../../types';
const OutputNode = ({ data, isConnectable, targetPosition = Position.Top }: NodeProps) => (
<>
+2 -1
View File
@@ -1,8 +1,9 @@
import { MouseEvent } from 'react';
import { StoreApi } from 'zustand';
import { HandleElement, Node, NodeOrigin, Position, ReactFlowState } from '../../types';
import { getDimensions } from '../../utils';
import { Position } from '../../types';
import type { HandleElement, Node, NodeOrigin, ReactFlowState } from '../../types';
export const getHandleBounds = (
selector: string,
+33 -13
View File
@@ -1,4 +1,5 @@
import { useEffect, useRef, memo, ComponentType, MouseEvent, KeyboardEvent } from 'react';
import { useEffect, useRef, memo } from 'react';
import type { ComponentType, MouseEvent, KeyboardEvent } from 'react';
import cc from 'classcat';
import { useStoreApi } from '../../hooks/useStore';
@@ -7,14 +8,14 @@ import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
import useDrag from '../../hooks/useDrag';
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
import { getMouseHandler, handleNodeClick } from './utils';
import { NodeProps, WrapNodeProps, XYPosition } from '../../types';
import { elementSelectionKeys } from '../../utils';
import type { NodeProps, WrapNodeProps, XYPosition } from '../../types';
export const arrowKeyDiffs: Record<string, XYPosition> = {
ArrowUp: { x: 0, y: -10 },
ArrowDown: { x: 0, y: 10 },
ArrowLeft: { x: -10, y: 0 },
ArrowRight: { x: 10, y: 0 },
ArrowUp: { x: 0, y: -1 },
ArrowDown: { x: 0, y: 1 },
ArrowLeft: { x: -1, y: 0 },
ArrowRight: { x: 1, y: 0 },
};
export default (NodeComponent: ComponentType<NodeProps>) => {
@@ -38,6 +39,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
isDraggable,
isSelectable,
isConnectable,
isFocusable,
selectNodesOnDrag,
sourcePosition,
targetPosition,
@@ -82,6 +84,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
};
const onKeyDown = (event: KeyboardEvent) => {
const { snapGrid, snapToGrid } = store.getState();
if (elementSelectionKeys.includes(event.key) && isSelectable) {
const unselect = event.key === 'Escape';
if (unselect) {
@@ -92,14 +95,28 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
store,
unselect,
});
} else if (selected && Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
} else if (
!disableKeyboardA11y &&
isDraggable &&
selected &&
Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)
) {
store.setState({
ariaLiveMessage: `Moved selected node ten pixels ${event.key
ariaLiveMessage: `Moved selected node ${event.key
.replace('Arrow', '')
.toLowerCase()}. New position, x: ${~~xPos}, y: ${~~yPos}`,
});
updatePositions(arrowKeyDiffs[event.key]);
// by default a node moves 5px on each key press, or 20px if shift is pressed
// if snap grid is enabled, we use that for the velocity.
const xVelo = snapToGrid ? snapGrid[0] : 5;
const yVelo = snapToGrid ? snapGrid[1] : 5;
const factor = event.shiftKey ? 4 : 1;
updatePositions({
x: arrowKeyDiffs[event.key].x * xVelo * factor,
y: arrowKeyDiffs[event.key].y * yVelo * factor,
});
}
};
@@ -151,13 +168,16 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
className={cc([
'react-flow__node',
`react-flow__node-${type}`,
{
// this is overwritable by passing `nopan` as a class name
[noPanClassName]: isDraggable,
},
className,
{
selected,
selectable: isSelectable,
parent: isParent,
dragging,
[noPanClassName]: isDraggable,
},
])}
ref={nodeRef}
@@ -176,9 +196,9 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
onContextMenu={onContextMenuHandler}
onClick={onSelectNodeHandler}
onDoubleClick={onDoubleClickHandler}
onKeyDown={disableKeyboardA11y ? undefined : onKeyDown}
tabIndex={disableKeyboardA11y ? undefined : 0}
role={disableKeyboardA11y ? undefined : 'button'}
onKeyDown={isFocusable ? onKeyDown : undefined}
tabIndex={isFocusable ? 0 : undefined}
role={isFocusable ? 'button' : undefined}
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`}
aria-label={ariaLabel}
>
@@ -3,16 +3,17 @@
* made a selection with on or several nodes
*/
import { memo, useRef, MouseEvent, KeyboardEvent, useEffect } from 'react';
import { memo, useRef, useEffect } from 'react';
import type { MouseEvent, KeyboardEvent } from 'react';
import cc from 'classcat';
import shallow from 'zustand/shallow';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { Node, ReactFlowState } from '../../types';
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;
+12 -4
View File
@@ -1,18 +1,26 @@
import { HTMLAttributes, ReactNode } from 'react';
import type { HTMLAttributes, ReactNode } from 'react';
import cc from 'classcat';
import { PanelPosition } from '../../types';
import { useStore } from '../../hooks/useStore';
import type { PanelPosition, ReactFlowState } from '../../types';
export type PanelProps = HTMLAttributes<HTMLDivElement> & {
position: PanelPosition;
children: ReactNode;
};
function Panel({ position, children, className, ...rest }: PanelProps) {
const selector = (s: ReactFlowState) => (s.userSelectionActive ? 'none' : 'all');
function Panel({ position, children, className, style, ...rest }: PanelProps) {
const pointerEvents = useStore(selector);
const positionClasses = `${position}`.split('-');
return (
<div className={cc(['react-flow__panel', className, ...positionClasses])} {...rest}>
<div
className={cc(['react-flow__panel', className, ...positionClasses])}
style={{ ...style, pointerEvents }}
{...rest}
>
{children}
</div>
);
@@ -1,9 +1,10 @@
import { FC, PropsWithChildren, useRef } from 'react';
import { useRef } from 'react';
import type { FC, PropsWithChildren } from 'react';
import { StoreApi } from 'zustand';
import { Provider } from '../../contexts/RFStoreContext';
import { createRFStore } from '../../store';
import { ReactFlowState } from '../../types';
import type { ReactFlowState } from '../../types';
const ReactFlowProvider: FC<PropsWithChildren> = ({ children }) => {
const storeRef = useRef<StoreApi<ReactFlowState> | null>(null);
@@ -1,12 +1,12 @@
import { memo, useEffect } from 'react';
import shallow from 'zustand/shallow';
import { ReactFlowState, OnSelectionChangeFunc, Node, Edge } from '../../types';
import { useStore, useStoreApi } from '../../hooks/useStore';
import type { ReactFlowState, OnSelectionChangeFunc, Node, Edge } from '../../types';
interface SelectionListenerProps {
onSelectionChange: OnSelectionChangeFunc;
}
type SelectionListenerProps = {
onSelectionChange?: OnSelectionChangeFunc;
};
const selector = (s: ReactFlowState) => ({
selectedNodes: Array.from(s.nodeInternals.values()).filter((n) => n.selected),
@@ -15,30 +15,42 @@ const selector = (s: ReactFlowState) => ({
type SelectorSlice = ReturnType<typeof selector>;
function areEqual(objA: SelectorSlice, objB: SelectorSlice) {
const selectedNodeIdsA = objA.selectedNodes.map((n: Node) => n.id);
const selectedNodeIdsB = objB.selectedNodes.map((n: Node) => n.id);
const selectId = (obj: Node | Edge) => obj.id;
const selectedEdgeIdsA = objA.selectedEdges.map((e: Edge) => e.id);
const selectedEdgeIdsB = objB.selectedEdges.map((e: Edge) => e.id);
return shallow(selectedNodeIdsA, selectedNodeIdsB) && shallow(selectedEdgeIdsA, selectedEdgeIdsB);
function areEqual(a: SelectorSlice, b: SelectorSlice) {
return (
shallow(a.selectedNodes.map(selectId), b.selectedNodes.map(selectId)) &&
shallow(a.selectedEdges.map(selectId), b.selectedEdges.map(selectId))
);
}
// This is just a helper component for calling the onSelectionChange listener.
// @TODO: Now that we have the onNodesChange and on EdgesChange listeners, do we still need this component?
function SelectionListener({ onSelectionChange }: SelectionListenerProps) {
const SelectionListener = memo(({ onSelectionChange }: SelectionListenerProps) => {
const store = useStoreApi();
const { selectedNodes, selectedEdges } = useStore(selector, areEqual);
useEffect(() => {
const params = { nodes: selectedNodes, edges: selectedEdges };
onSelectionChange(params);
onSelectionChange?.(params);
store.getState().onSelectionChange?.(params);
}, [selectedNodes, selectedEdges]);
}, [selectedNodes, selectedEdges, onSelectionChange]);
return null;
});
SelectionListener.displayName = 'SelectionListener';
const changeSelector = (s: ReactFlowState) => !!s.onSelectionChange;
function Wrapper({ onSelectionChange }: SelectionListenerProps) {
const storeHasSelectionChange = useStore(changeSelector);
if (onSelectionChange || storeHasSelectionChange) {
return <SelectionListener onSelectionChange={onSelectionChange} />;
}
return null;
}
export default memo(SelectionListener);
export default Wrapper;
@@ -3,7 +3,7 @@ import { StoreApi } from 'zustand';
import shallow from 'zustand/shallow';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { Node, Edge, ReactFlowState, CoordinateExtent, ReactFlowProps, ReactFlowStore } from '../../types';
import type { Node, Edge, ReactFlowState, CoordinateExtent, ReactFlowProps, ReactFlowStore } from '../../types';
type StoreUpdaterProps = Pick<
ReactFlowProps,
@@ -18,6 +18,8 @@ type StoreUpdaterProps = Pick<
| 'onClickConnectEnd'
| 'nodesDraggable'
| 'nodesConnectable'
| 'nodesFocusable'
| 'edgesFocusable'
| 'minZoom'
| 'maxZoom'
| 'nodeExtent'
@@ -42,8 +44,7 @@ type StoreUpdaterProps = Pick<
| 'onSelectionDragStop'
| 'noPanClassName'
| 'nodeOrigin'
| 'id'
>;
> & { rfId: string };
const selector = (s: ReactFlowState) => ({
setNodes: s.setNodes,
@@ -89,6 +90,8 @@ const StoreUpdater = ({
onClickConnectEnd,
nodesDraggable,
nodesConnectable,
nodesFocusable,
edgesFocusable,
minZoom,
maxZoom,
nodeExtent,
@@ -113,7 +116,7 @@ const StoreUpdater = ({
onSelectionDragStop,
noPanClassName,
nodeOrigin,
id,
rfId,
}: StoreUpdaterProps) => {
const {
setNodes,
@@ -145,6 +148,8 @@ const StoreUpdater = ({
useDirectStoreUpdater('onClickConnectEnd', onClickConnectEnd, store.setState);
useDirectStoreUpdater('nodesDraggable', nodesDraggable, store.setState);
useDirectStoreUpdater('nodesConnectable', nodesConnectable, store.setState);
useDirectStoreUpdater('nodesFocusable', nodesFocusable, store.setState);
useDirectStoreUpdater('edgesFocusable', edgesFocusable, store.setState);
useDirectStoreUpdater('elementsSelectable', elementsSelectable, store.setState);
useDirectStoreUpdater('snapToGrid', snapToGrid, store.setState);
useDirectStoreUpdater('snapGrid', snapGrid, store.setState);
@@ -163,7 +168,7 @@ const StoreUpdater = ({
useDirectStoreUpdater('onSelectionDragStop', onSelectionDragStop, store.setState);
useDirectStoreUpdater('noPanClassName', noPanClassName, store.setState);
useDirectStoreUpdater('nodeOrigin', nodeOrigin, store.setState);
useDirectStoreUpdater('rfId', id, store.setState);
useDirectStoreUpdater('rfId', rfId, store.setState);
useStoreUpdater<Node[]>(nodes, setNodes);
useStoreUpdater<Edge[]>(edges, setEdges);
@@ -7,8 +7,8 @@ import shallow from 'zustand/shallow';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { getSelectionChanges } from '../../utils/changes';
import { XYPosition, ReactFlowState, NodeChange, EdgeChange, Rect } from '../../types';
import { getConnectedEdges, getNodesInside } from '../../utils/graph';
import type { XYPosition, ReactFlowState, NodeChange, EdgeChange, Rect } from '../../types';
type SelectionRect = Rect & {
startX: number;
@@ -1,16 +1,18 @@
import { memo, useCallback } from 'react';
import { useStore } from '../../hooks/useStore';
import { EdgeMarker, ReactFlowState } from '../../types';
import { getMarkerId } from '../../utils/graph';
import { useMarkerSymbol } from './MarkerSymbols';
interface MarkerProps extends EdgeMarker {
import type { EdgeMarker, ReactFlowState } from '../../types';
type MarkerProps = EdgeMarker & {
id: string;
}
interface MarkerDefinitionsProps {
};
type MarkerDefinitionsProps = {
defaultColor: string;
rfId?: string;
}
};
const Marker = ({
id,
@@ -1,6 +1,8 @@
import { useMemo } from 'react';
import { MarkerType, EdgeMarker } from '../../types';
import { devWarn } from '../../utils';
import { MarkerType } from '../../types';
import type { EdgeMarker } from '../../types';
type SymbolProps = Omit<EdgeMarker, 'type'>;
@@ -8,44 +8,45 @@ import ConnectionLine from '../../components/ConnectionLine/index';
import MarkerDefinitions from './MarkerDefinitions';
import { getEdgePositions, getHandle, getNodeData } from './utils';
import { Position, Edge, ConnectionMode, ReactFlowState } from '../../types';
import { GraphViewProps } from '../GraphView';
import { devWarn } from '../../utils';
import { ConnectionMode, Position } from '../../types';
import type { Edge, ReactFlowState } from '../../types';
interface EdgeRendererProps
extends Pick<
GraphViewProps,
| 'edgeTypes'
| 'connectionLineType'
| 'connectionLineType'
| 'connectionLineStyle'
| 'connectionLineComponent'
| 'connectionLineContainerStyle'
| 'connectionLineContainerStyle'
| 'onEdgeClick'
| 'onEdgeDoubleClick'
| 'defaultMarkerColor'
| 'onlyRenderVisibleElements'
| 'onEdgeUpdate'
| 'onEdgeContextMenu'
| 'onEdgeMouseEnter'
| 'onEdgeMouseMove'
| 'onEdgeMouseLeave'
| 'onEdgeUpdateStart'
| 'onEdgeUpdateEnd'
| 'edgeUpdaterRadius'
| 'noPanClassName'
| 'elevateEdgesOnSelect'
| 'rfId'
| 'disableKeyboardA11y'
> {
type EdgeRendererProps = Pick<
GraphViewProps,
| 'edgeTypes'
| 'connectionLineType'
| 'connectionLineType'
| 'connectionLineStyle'
| 'connectionLineComponent'
| 'connectionLineContainerStyle'
| 'connectionLineContainerStyle'
| 'onEdgeClick'
| 'onEdgeDoubleClick'
| 'defaultMarkerColor'
| 'onlyRenderVisibleElements'
| 'onEdgeUpdate'
| 'onEdgeContextMenu'
| 'onEdgeMouseEnter'
| 'onEdgeMouseMove'
| 'onEdgeMouseLeave'
| 'onEdgeUpdateStart'
| 'onEdgeUpdateEnd'
| 'edgeUpdaterRadius'
| 'noPanClassName'
| 'elevateEdgesOnSelect'
| 'rfId'
| 'disableKeyboardA11y'
> & {
elevateEdgesOnSelect: boolean;
}
};
const selector = (s: ReactFlowState) => ({
connectionNodeId: s.connectionNodeId,
connectionHandleType: s.connectionHandleType,
nodesConnectable: s.nodesConnectable,
edgesFocusable: s.edgesFocusable,
elementsSelectable: s.elementsSelectable,
width: s.width,
height: s.height,
@@ -58,6 +59,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
connectionNodeId,
connectionHandleType,
nodesConnectable,
edgesFocusable,
elementsSelectable,
width,
height,
@@ -119,6 +121,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
const targetHandle = getHandle(targetNodeHandles!, edge.targetHandle || null);
const sourcePosition = sourceHandle?.position || Position.Bottom;
const targetPosition = targetHandle?.position || Position.Top;
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
if (!sourceHandle || !targetHandle) {
devWarn(
@@ -181,7 +184,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
onEdgeUpdateEnd={props.onEdgeUpdateEnd}
rfId={props.rfId}
ariaLabel={edge.ariaLabel}
disableKeyboardA11y={props.disableKeyboardA11y}
isFocusable={isFocusable}
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
interactionWidth={edge.interactionWidth}
/>
@@ -1,19 +1,20 @@
import { ComponentType } from 'react';
import type { ComponentType } from 'react';
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges';
import wrapEdge from '../../components/Edges/wrapEdge';
import {
import { internalsSymbol, rectToBox } from '../../utils';
import { Position } from '../../types';
import type {
EdgeProps,
EdgeTypes,
EdgeTypesWrapped,
HandleElement,
NodeHandleBounds,
Node,
Position,
Rect,
Transform,
XYPosition,
} from '../../types';
import { internalsSymbol, rectToBox } from '../../utils';
export type CreateEdgeTypes = (edgeTypes: EdgeTypes) => EdgeTypesWrapped;
@@ -1,4 +1,4 @@
import { MouseEvent } from 'react';
import type { MouseEvent } from 'react';
import cc from 'classcat';
import { useStore } from '../../hooks/useStore';
@@ -1,4 +1,5 @@
import { memo, ReactNode, WheelEvent, MouseEvent } from 'react';
import { memo } from 'react';
import type { ReactNode, WheelEvent, MouseEvent } from 'react';
import { useStore, useStoreApi } from '../../hooks/useStore';
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
@@ -8,8 +9,7 @@ import ZoomPane from '../ZoomPane';
import UserSelection from '../../components/UserSelection';
import NodesSelection from '../../components/NodesSelection';
import Pane from './Pane';
import { ReactFlowState } from '../../types';
import type { ReactFlowState } from '../../types';
export type FlowRendererProps = Omit<
GraphViewProps,
+28 -32
View File
@@ -5,39 +5,35 @@ import NodeRenderer from '../NodeRenderer';
import EdgeRenderer from '../EdgeRenderer';
import ViewportWrapper from '../Viewport';
import useOnInitHandler from '../../hooks/useOnInitHandler';
import {
NodeTypesWrapped,
EdgeTypesWrapped,
ConnectionLineType,
KeyCode,
ReactFlowProps,
Viewport,
CoordinateExtent,
NodeOrigin,
} from '../../types';
import type { EdgeTypesWrapped, NodeTypesWrapped, ReactFlowProps } from '../../types';
export interface GraphViewProps
extends Omit<ReactFlowProps, 'onSelectionChange' | 'nodes' | 'edges' | 'nodeTypes' | 'edgeTypes'> {
nodeTypes: NodeTypesWrapped;
edgeTypes: EdgeTypesWrapped;
selectionKeyCode: KeyCode | null;
deleteKeyCode: KeyCode | null;
multiSelectionKeyCode: KeyCode | null;
connectionLineType: ConnectionLineType;
onlyRenderVisibleElements: boolean;
translateExtent: CoordinateExtent;
minZoom: number;
maxZoom: number;
defaultMarkerColor: string;
selectNodesOnDrag: boolean;
noDragClassName: string;
noWheelClassName: string;
noPanClassName: string;
defaultViewport: Viewport;
rfId: string;
disableKeyboardA11y: boolean;
nodeOrigin: NodeOrigin;
}
export type GraphViewProps = Omit<ReactFlowProps, 'onSelectionChange' | 'nodes' | 'edges' | 'nodeTypes' | 'edgeTypes'> &
Required<
Pick<
ReactFlowProps,
| 'selectionKeyCode'
| 'deleteKeyCode'
| 'multiSelectionKeyCode'
| 'connectionLineType'
| 'onlyRenderVisibleElements'
| 'translateExtent'
| 'minZoom'
| 'maxZoom'
| 'defaultMarkerColor'
| 'selectNodesOnDrag'
| 'noDragClassName'
| 'noDragClassName'
| 'noWheelClassName'
| 'noPanClassName'
| 'defaultViewport'
| 'disableKeyboardA11y'
| 'nodeOrigin'
>
> & {
nodeTypes: NodeTypesWrapped;
edgeTypes: EdgeTypesWrapped;
rfId: string;
};
const GraphView = ({
nodeTypes,
@@ -1,4 +1,5 @@
import { memo, useMemo, ComponentType, useEffect, useRef } from 'react';
import { memo, useMemo, useEffect, useRef } from 'react';
import type { ComponentType } from 'react';
import shallow from 'zustand/shallow';
import useVisibleNodes from '../../hooks/useVisibleNodes';
@@ -6,8 +7,9 @@ import { useStore } from '../../hooks/useStore';
import { clampPosition, devWarn, internalsSymbol } from '../../utils';
import { containerStyle } from '../../styles';
import { GraphViewProps } from '../GraphView';
import { Position, ReactFlowState, WrapNodeProps } from '../../types';
import { getPositionWithOrigin } from './utils';
import { Position } from '../../types';
import type { ReactFlowState, WrapNodeProps } from '../../types';
type NodeRendererProps = Pick<
GraphViewProps,
@@ -31,12 +33,16 @@ type NodeRendererProps = Pick<
const selector = (s: ReactFlowState) => ({
nodesDraggable: s.nodesDraggable,
nodesConnectable: s.nodesConnectable,
nodesFocusable: s.nodesFocusable,
elementsSelectable: s.elementsSelectable,
updateNodeDimensions: s.updateNodeDimensions,
});
const NodeRenderer = (props: NodeRendererProps) => {
const { nodesDraggable, nodesConnectable, elementsSelectable, updateNodeDimensions } = useStore(selector, shallow);
const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, updateNodeDimensions } = useStore(
selector,
shallow
);
const nodes = useVisibleNodes(props.onlyRenderVisibleElements);
const resizeObserverRef = useRef<ResizeObserver>();
@@ -82,6 +88,8 @@ const NodeRenderer = (props: NodeRendererProps) => {
const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined'));
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined'));
const clampedPosition = props.nodeExtent
? clampPosition(node.positionAbsolute, props.nodeExtent)
: node.positionAbsolute;
@@ -122,6 +130,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
isDraggable={isDraggable}
isSelectable={isSelectable}
isConnectable={isConnectable}
isFocusable={isFocusable}
resizeObserver={resizeObserver}
dragHandle={node.dragHandle}
zIndex={node[internalsSymbol]?.z ?? 0}
@@ -1,12 +1,12 @@
import { ComponentType } from 'react';
import type { ComponentType } from 'react';
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 { NodeTypes, NodeProps, NodeTypesWrapped, NodeOrigin, XYPosition } from '../../types';
import { devWarn } from '../../utils';
import type { NodeTypes, NodeProps, NodeTypesWrapped, NodeOrigin, XYPosition } from '../../types';
export type CreateNodeTypes = (nodeTypes: NodeTypes) => NodeTypesWrapped;
@@ -1,4 +1,4 @@
import { FC, PropsWithChildren } from 'react';
import type { FC, PropsWithChildren } from 'react';
import { useStoreApi } from '../../hooks/useStore';
import ReactFlowProvider from '../../components/ReactFlowProvider';
+22 -18
View File
@@ -1,4 +1,5 @@
import { CSSProperties, forwardRef } from 'react';
import { forwardRef } from 'react';
import type { CSSProperties } from 'react';
import cc from 'classcat';
import Attribution from '../../components/Attribution';
@@ -9,27 +10,24 @@ import OutputNode from '../../components/Nodes/OutputNode';
import GroupNode from '../../components/Nodes/GroupNode';
import SelectionListener from '../../components/SelectionListener';
import StoreUpdater from '../../components/StoreUpdater';
import {
ConnectionLineType,
ConnectionMode,
import A11yDescriptions from '../../components/A11yDescriptions';
import { createEdgeTypes } from '../EdgeRenderer/utils';
import { createNodeTypes } from '../NodeRenderer/utils';
import GraphView from '../GraphView';
import Wrapper from './Wrapper';
import { infiniteExtent } from '../../store/initialState';
import { useNodeOrEdgeTypes } from './utils';
import { ConnectionLineType, ConnectionMode, PanOnScrollMode } from '../../types';
import type {
EdgeTypes,
EdgeTypesWrapped,
NodeOrigin,
NodeTypes,
NodeTypesWrapped,
PanOnScrollMode,
ReactFlowProps,
ReactFlowRefType,
Viewport,
} from '../../types';
import { createEdgeTypes } from '../EdgeRenderer/utils';
import GraphView from '../GraphView';
import { createNodeTypes } from '../NodeRenderer/utils';
import { useNodeOrEdgeTypes } from './utils';
import Wrapper from './Wrapper';
import A11yDescriptions from '../../components/A11yDescriptions';
import { infiniteExtent } from '../../store/initialState';
const defaultNodeTypes: NodeTypes = {
input: InputNode,
@@ -108,7 +106,9 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
selectNodesOnDrag = true,
nodesDraggable,
nodesConnectable,
nodesFocusable,
nodeOrigin = initNodeOrigin,
edgesFocusable,
elementsSelectable,
defaultViewport = initDefaultViewport,
minZoom = 0.5,
@@ -154,13 +154,14 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
elevateEdgesOnSelect = false,
disableKeyboardA11y = false,
style,
id = '1',
id,
...rest
},
ref
) => {
const nodeTypesWrapped = useNodeOrEdgeTypes(nodeTypes, createNodeTypes) as NodeTypesWrapped;
const edgeTypesWrapped = useNodeOrEdgeTypes(edgeTypes, createEdgeTypes) as EdgeTypesWrapped;
const rfId = id || '1';
return (
<div
@@ -169,6 +170,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
ref={ref}
className={cc(['react-flow', className])}
data-testid="rf__wrapper"
id={id}
>
<Wrapper>
<GraphView
@@ -228,7 +230,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
noWheelClassName={noWheelClassName}
noPanClassName={noPanClassName}
elevateEdgesOnSelect={elevateEdgesOnSelect}
rfId={id}
rfId={rfId}
disableKeyboardA11y={disableKeyboardA11y}
nodeOrigin={nodeOrigin}
nodeExtent={nodeExtent}
@@ -245,6 +247,8 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
onClickConnectEnd={onClickConnectEnd}
nodesDraggable={nodesDraggable}
nodesConnectable={nodesConnectable}
nodesFocusable={nodesFocusable}
edgesFocusable={edgesFocusable}
elementsSelectable={elementsSelectable}
minZoom={minZoom}
maxZoom={maxZoom}
@@ -269,12 +273,12 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
onSelectionDragStop={onSelectionDragStop}
noPanClassName={noPanClassName}
nodeOrigin={nodeOrigin}
id={id}
rfId={rfId}
/>
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
<SelectionListener onSelectionChange={onSelectionChange} />
{children}
<Attribution proOptions={proOptions} position={attributionPosition} />
{!disableKeyboardA11y && <A11yDescriptions rfId={id} />}
<A11yDescriptions rfId={rfId} disableKeyboardA11y={disableKeyboardA11y} />
</Wrapper>
</div>
);
@@ -1,10 +1,10 @@
import { useMemo, useRef } from 'react';
import shallow from 'zustand/shallow';
import { EdgeTypes, EdgeTypesWrapped, NodeTypes, NodeTypesWrapped } from '../../types';
import { devWarn } from '../../utils';
import { CreateEdgeTypes } from '../EdgeRenderer/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: EdgeTypes, createTypes: CreateEdgeTypes): EdgeTypesWrapped;
@@ -1,7 +1,7 @@
import { ReactNode } from 'react';
import type { ReactNode } from 'react';
import { useStore } from '../../hooks/useStore';
import { ReactFlowState } from '../../types';
import type { ReactFlowState } from '../../types';
const selector = (s: ReactFlowState) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`;
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useRef } from 'react';
import { D3ZoomEvent, zoom, zoomIdentity } from 'd3-zoom';
import { zoom, zoomIdentity } from 'd3-zoom';
import type { D3ZoomEvent } from 'd3-zoom';
import { select, pointer } from 'd3-selection';
import shallow from 'zustand/shallow';
@@ -8,9 +9,10 @@ import { clamp } from '../../utils';
import useKeyPress from '../../hooks/useKeyPress';
import useResizeHandler from '../../hooks/useResizeHandler';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { FlowRendererProps } from '../FlowRenderer';
import { containerStyle } from '../../styles';
import { Viewport, PanOnScrollMode, ReactFlowState } from '../../types';
import type { FlowRendererProps } from '../FlowRenderer';
import { PanOnScrollMode } from '../../types';
import type { Viewport, ReactFlowState } from '../../types';
type ZoomPaneProps = Omit<
FlowRendererProps,
+3 -2
View File
@@ -1,12 +1,13 @@
import { RefObject, useEffect, useRef, MouseEvent, useState, useCallback } from 'react';
import { useEffect, useRef, useState, useCallback } from 'react';
import type { RefObject, MouseEvent } from 'react';
import { drag } from 'd3-drag';
import { select } from 'd3-selection';
import type { D3DragEvent, SubjectPosition } from 'd3';
import { useStoreApi } from '../../hooks/useStore';
import { NodeDragItem, Node, SelectionDragHandler } from '../../types';
import { getDragItems, getEventHandlerParams, hasSelector, calcNextPosition } from './utils';
import { handleNodeClick } from '../../components/Nodes/utils';
import type { NodeDragItem, Node, SelectionDragHandler } from '../../types';
export type UseDragEvent = D3DragEvent<HTMLDivElement, null, SubjectPosition>;
export type UseDragData = { dx: number; dy: number };
+2 -2
View File
@@ -1,7 +1,7 @@
import { RefObject } from 'react';
import type { RefObject } from 'react';
import { CoordinateExtent, Node, NodeDragItem, NodeInternals, XYPosition } from '../../types';
import { clampPosition, devWarn } from '../../utils';
import type { CoordinateExtent, Node, NodeDragItem, NodeInternals, XYPosition } from '../../types';
export function isParentSelected(node: Node, nodeInternals: NodeInternals): boolean {
if (!node.parentNode) {
+1 -1
View File
@@ -1,5 +1,5 @@
import { useStore } from '../hooks/useStore';
import { Edge, ReactFlowState } from '../types';
import type { Edge, ReactFlowState } from '../types';
const edgesSelector = (state: ReactFlowState) => state.edges;
@@ -2,9 +2,8 @@ import { useEffect } from 'react';
import { useStoreApi } from '../hooks/useStore';
import useKeyPress from './useKeyPress';
import { KeyCode } from '../types';
import type { KeyCode } from '../types';
import useReactFlow from './useReactFlow';
interface HookParams {
deleteKeyCode: KeyCode | null;
multiSelectionKeyCode: KeyCode | null;
@@ -19,13 +18,10 @@ export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => {
useEffect(() => {
if (deleteKeyPressed) {
const {
nodeInternals,
edges,
} = store.getState();
const { nodeInternals, edges } = store.getState();
const nodes = Array.from(nodeInternals.values());
const nodeIds = nodes.filter(node => node.selected).map(node => node.id);
const edgeIds = edges.filter(edge => edge.selected).map(edge => edge.id);
const nodeIds = nodes.filter((node) => node.selected).map((node) => node.id);
const edgeIds = edges.filter((edge) => edge.selected).map((edge) => edge.id);
deleteElements(nodeIds, edgeIds);
store.setState({ nodesSelectionActive: false });
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { useState, useEffect, useRef, useMemo } from 'react';
import { KeyCode } from '../types';
import type { KeyCode } from '../types';
type Keys = Array<string>;
type PressedKeys = Set<string>;
+1 -1
View File
@@ -1,5 +1,5 @@
import { useStore } from '../hooks/useStore';
import { Node, ReactFlowState } from '../types';
import type { Node, ReactFlowState } from '../types';
const nodesSelector = (state: ReactFlowState) => Array.from(state.nodeInternals.values());
@@ -1,8 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useState, useCallback, SetStateAction, Dispatch } from 'react';
import { useState, useCallback } from 'react';
import type { SetStateAction, Dispatch } from 'react';
import { applyNodeChanges, applyEdgeChanges } from '../utils/changes';
import { Node, NodeChange, Edge, EdgeChange } from '../types';
import type { Node, NodeChange, Edge, EdgeChange } from '../types';
type ApplyChanges<ItemType, ChangesType> = (changes: ChangesType[], items: ItemType[]) => ItemType[];
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
@@ -1,6 +1,6 @@
import { ReactFlowState } from '../types';
import { internalsSymbol } from '../utils';
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 useReactFlow from './useReactFlow';
import { OnInit } from '../types';
import type { OnInit } from '../types';
function useOnInitHandler(onInit: OnInit | undefined) {
const rfInstance = useReactFlow();
+1 -1
View File
@@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react';
import useViewportHelper from './useViewportHelper';
import { useStoreApi } from '../hooks/useStore';
import {
import type {
ReactFlowInstance,
Instance,
NodeAddChange,
+2 -1
View File
@@ -1,4 +1,5 @@
import { useEffect, MutableRefObject } from 'react';
import { useEffect } from 'react';
import type { MutableRefObject } from 'react';
import { useStoreApi } from '../hooks/useStore';
import { devWarn, getDimensions } from '../utils';
+3 -2
View File
@@ -1,8 +1,9 @@
import { useContext, useMemo } from 'react';
import { StoreApi, useStore as useZustandStore } from 'zustand';
import { useStore as useZustandStore } from 'zustand';
import type { StoreApi } from 'zustand';
import StoreContext from '../contexts/RFStoreContext';
import { ReactFlowState } from '../types';
import type { ReactFlowState } from '../types';
const errorMessage =
'[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#100';
@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import { useStoreApi } from '../hooks/useStore';
import { UpdateNodeInternals } from '../types';
import type { UpdateNodeInternals } from '../types';
function useUpdateNodeInternals(): UpdateNodeInternals {
const store = useStoreApi();
@@ -2,24 +2,25 @@ import { useCallback } from 'react';
import { useStoreApi } from '../hooks/useStore';
import { calcNextPosition } from './useDrag/utils';
import { XYPosition } from '../types';
import type { XYPosition } from '../types';
function useUpdateNodePositions() {
const store = useStoreApi();
const updatePositions = useCallback((positionDiff: XYPosition) => {
const { nodeInternals, nodeExtent, updateNodePositions } = store.getState();
const { nodeInternals, nodeExtent, updateNodePositions, snapToGrid, snapGrid } = store.getState();
const selectedNodes = Array.from(nodeInternals.values()).filter((n) => n.selected);
const nodeUpdates = selectedNodes.map((n) => {
if (n.positionAbsolute) {
const updatedPos = calcNextPosition(
n,
{ x: n.positionAbsolute.x + positionDiff.x, y: n.positionAbsolute.y + positionDiff.y },
nodeInternals,
nodeExtent
);
const nextPosition = { x: n.positionAbsolute.x + positionDiff.x, y: n.positionAbsolute.y + positionDiff.y };
if (snapToGrid) {
nextPosition.x = snapGrid[0] * Math.round(nextPosition.x / snapGrid[0]);
nextPosition.y = snapGrid[1] * Math.round(nextPosition.y / snapGrid[1]);
}
const updatedPos = calcNextPosition(n, nextPosition, nodeInternals, nodeExtent);
n.position = updatedPos.position;
n.positionAbsolute = updatedPos.positionAbsolute;
+1 -1
View File
@@ -1,7 +1,7 @@
import shallow from 'zustand/shallow';
import { useStore } from '../hooks/useStore';
import { Viewport, ReactFlowState } from '../types';
import type { Viewport, ReactFlowState } from '../types';
const viewportSelector = (state: ReactFlowState) => ({
x: state.transform[0],
+1 -1
View File
@@ -4,8 +4,8 @@ import shallow from 'zustand/shallow';
import { useStoreApi, useStore } from '../hooks/useStore';
import { pointToRendererPoint, getTransformForBounds, getD3Transition } from '../utils/graph';
import { ViewportHelperFunctions, ReactFlowState, XYPosition } from '../types';
import { fitView as fitViewStore } from '../store/utils';
import type { ViewportHelperFunctions, ReactFlowState, XYPosition } from '../types';
// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};
+1 -1
View File
@@ -2,8 +2,8 @@ import { useCallback } from 'react';
import { useStore } from '../hooks/useStore';
import { isEdgeVisible } from '../container/EdgeRenderer/utils';
import { ReactFlowState, NodeInternals, Edge } from '../types';
import { internalsSymbol, isNumeric } from '../utils';
import type { ReactFlowState, NodeInternals, Edge } from '../types';
const defaultEdgeTree = [{ level: 0, isMaxLevel: true, edges: [] }];
+1 -2
View File
@@ -2,8 +2,7 @@ import { useCallback } from 'react';
import { useStore } from '../hooks/useStore';
import { getNodesInside } from '../utils/graph';
import { ReactFlowState } from '../types';
import type { ReactFlowState } from '../types';
function useVisibleNodes(onlyRenderVisible: boolean) {
const nodes = useStore(
+10 -6
View File
@@ -1,8 +1,11 @@
import { createStore } from 'zustand';
import { clampPosition, getDimensions, internalsSymbol } from '../utils';
import { applyNodeChanges } from '../utils/changes';
import {
import { applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
import { getHandleBounds } from '../components/Nodes/utils';
import { createNodeInternals, fitView, updateNodesAndEdgesSelections } from './utils';
import initialState from './initialState';
import type {
ReactFlowState,
Node,
Edge,
@@ -15,10 +18,6 @@ import {
NodeDragItem,
UnselectNodesAndEdgesParams,
} from '../types';
import { getHandleBounds } from '../components/Nodes/utils';
import { createSelectionChange, getSelectionChanges } from '../utils/changes';
import { createNodeInternals, fitView, updateNodesAndEdgesSelections } from './utils';
import initialState from './initialState';
const createRFStore = () =>
createStore<ReactFlowState>((set, get) => ({
@@ -238,6 +237,11 @@ const createRFStore = () =>
nodeInternals: new Map(nodeInternals),
});
},
cancelConnection: () =>
set({
connectionNodeId: initialState.connectionNodeId,
connectionHandleId: initialState.connectionHandleId,
}),
reset: () => set({ ...initialState }),
}));
+4 -1
View File
@@ -1,4 +1,5 @@
import { CoordinateExtent, ReactFlowStore, ConnectionMode } from '../types';
import { ConnectionMode } from '../types';
import type { CoordinateExtent, ReactFlowStore } from '../types';
export const infiniteExtent: CoordinateExtent = [
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
@@ -40,6 +41,8 @@ const initialState: ReactFlowStore = {
nodesDraggable: true,
nodesConnectable: true,
nodesFocusable: true,
edgesFocusable: true,
elementsSelectable: true,
fitViewOnInit: false,
fitViewOnInitDone: false,
+2 -2
View File
@@ -1,9 +1,9 @@
import { zoomIdentity } from 'd3-zoom';
import { StoreApi } from 'zustand';
import type { StoreApi } from 'zustand';
import { internalsSymbol, isNumeric } from '../utils';
import { getD3Transition, getRectOfNodes, getTransformForBounds } from '../utils/graph';
import {
import type {
Edge,
EdgeSelectionChange,
Node,
+1 -1
View File
@@ -1,4 +1,4 @@
import { CSSProperties } from 'react';
import type { CSSProperties } from 'react';
export const containerStyle: CSSProperties = {
position: 'absolute',
+5
View File
@@ -56,6 +56,11 @@
animation: dashdraw 0.5s linear infinite;
}
&.animated path.react-flow__edge-interaction {
stroke-dasharray: none;
animation: none;
}
&.inactive {
pointer-events: none;
}
+3 -3
View File
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { XYPosition, Dimensions } from './utils';
import { Node } from './nodes';
import { Edge } from './edges';
import type { XYPosition, Dimensions } from './utils';
import type { Node } from './nodes';
import type { Edge } from './edges';
export type NodeDimensionChange = {
id: string;
+24 -21
View File
@@ -1,6 +1,6 @@
import React, { CSSProperties, HTMLAttributes, MouseEvent as ReactMouseEvent, WheelEvent } from 'react';
import type { CSSProperties, HTMLAttributes, MouseEvent as ReactMouseEvent, WheelEvent } from 'react';
import {
import type {
OnSelectionChangeFunc,
NodeTypes,
EdgeTypes,
@@ -33,19 +33,17 @@ import {
SelectionDragHandler,
Viewport,
NodeOrigin,
EdgeMouseHandler,
HandleType,
} from '.';
import { HandleType } from './handles';
export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
export type ReactFlowProps = HTMLAttributes<HTMLDivElement> & {
nodes?: Node[];
edges?: Edge[];
defaultNodes?: Node[];
defaultEdges?: Edge[];
defaultEdgeOptions?: DefaultEdgeOptions;
onNodesChange?: OnNodesChange;
onEdgesChange?: OnEdgesChange;
onNodeClick?: NodeMouseHandler;
onEdgeClick?: (event: React.MouseEvent, node: Edge) => void;
onNodeDoubleClick?: NodeMouseHandler;
onNodeMouseEnter?: NodeMouseHandler;
onNodeMouseMove?: NodeMouseHandler;
@@ -54,8 +52,23 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
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, edge: Edge, handleType: HandleType) => void;
onNodesChange?: OnNodesChange;
onEdgesChange?: OnEdgesChange;
onNodesDelete?: OnNodesDelete;
onEdgesDelete?: OnEdgesDelete;
onSelectionDragStart?: SelectionDragHandler;
onSelectionDrag?: SelectionDragHandler;
onSelectionDragStop?: SelectionDragHandler;
onSelectionContextMenu?: (event: ReactMouseEvent, nodes: Node[]) => void;
onConnect?: OnConnect;
onConnectStart?: OnConnectStart;
onConnectEnd?: OnConnectEnd;
@@ -66,10 +79,6 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
onMoveStart?: OnMoveStart;
onMoveEnd?: OnMoveEnd;
onSelectionChange?: OnSelectionChangeFunc;
onSelectionDragStart?: SelectionDragHandler;
onSelectionDrag?: SelectionDragHandler;
onSelectionDragStop?: SelectionDragHandler;
onSelectionContextMenu?: (event: ReactMouseEvent, nodes: Node[]) => void;
onPaneScroll?: (event?: WheelEvent) => void;
onPaneClick?: (event: ReactMouseEvent) => void;
onPaneContextMenu?: (event: ReactMouseEvent) => void;
@@ -78,11 +87,11 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
onPaneMouseLeave?: (event: ReactMouseEvent) => void;
nodeTypes?: NodeTypes;
edgeTypes?: EdgeTypes;
connectionMode?: ConnectionMode;
connectionLineType?: ConnectionLineType;
connectionLineStyle?: CSSProperties;
connectionLineComponent?: ConnectionLineComponent;
connectionLineContainerStyle?: CSSProperties;
connectionMode?: ConnectionMode;
deleteKeyCode?: KeyCode | null;
selectionKeyCode?: KeyCode | null;
multiSelectionKeyCode?: KeyCode | null;
@@ -92,7 +101,9 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
onlyRenderVisibleElements?: boolean;
nodesDraggable?: boolean;
nodesConnectable?: boolean;
nodesFocusable?: boolean;
nodeOrigin?: NodeOrigin;
edgesFocusable?: boolean;
initNodeOrigin?: NodeOrigin;
elementsSelectable?: boolean;
selectNodesOnDrag?: boolean;
@@ -110,14 +121,6 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
panOnScrollSpeed?: number;
panOnScrollMode?: PanOnScrollMode;
zoomOnDoubleClick?: boolean;
onEdgeUpdate?: OnEdgeUpdateFunc;
onEdgeContextMenu?: (event: ReactMouseEvent, edge: Edge) => void;
onEdgeMouseEnter?: (event: ReactMouseEvent, edge: Edge) => void;
onEdgeMouseMove?: (event: ReactMouseEvent, edge: Edge) => void;
onEdgeMouseLeave?: (event: ReactMouseEvent, edge: Edge) => void;
onEdgeDoubleClick?: (event: ReactMouseEvent, edge: Edge) => void;
onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge, handleType: HandleType) => void;
onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge, handleType: HandleType) => void;
edgeUpdaterRadius?: number;
noDragClassName?: string;
noWheelClassName?: string;
@@ -129,6 +132,6 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
proOptions?: ProOptions;
elevateEdgesOnSelect?: boolean;
disableKeyboardA11y?: boolean;
}
};
export type ReactFlowRefType = HTMLDivElement;
+62 -80
View File
@@ -1,9 +1,17 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { CSSProperties, ComponentType, HTMLAttributes, ReactNode } from 'react';
import { Connection } from './general';
import { HandleElement, HandleType } from './handles';
import { Node } from './nodes';
import { Position } from './utils';
import type { CSSProperties, ComponentType, HTMLAttributes, ReactNode, MouseEvent as ReactMouseEvent } from 'react';
import { 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> = {
@@ -13,12 +21,6 @@ type DefaultEdge<T = any> = {
target: string;
sourceHandle?: string | null;
targetHandle?: string | null;
label?: string | ReactNode;
labelStyle?: CSSProperties;
labelShowBg?: boolean;
labelBgStyle?: CSSProperties;
labelBgPadding?: [number, number];
labelBgBorderRadius?: number;
style?: CSSProperties;
animated?: boolean;
hidden?: boolean;
@@ -33,7 +35,8 @@ type DefaultEdge<T = any> = {
zIndex?: number;
ariaLabel?: string;
interactionWidth?: number;
};
focusable?: boolean;
} & EdgeLabelOptions;
export type SmoothStepPathOptions = {
offset?: number;
@@ -61,55 +64,7 @@ export type DefaultEdgeOptions = Omit<
'id' | 'source' | 'target' | 'sourceHandle' | 'targetHandle' | 'sourceNode' | 'targetNode'
>;
// props that get passed to a custom edge
export type EdgeProps<T = any> = {
id: string;
source: string;
target: string;
sourceX: number;
sourceY: number;
targetX: number;
targetY: number;
selected?: boolean;
animated?: boolean;
sourcePosition: Position;
targetPosition: Position;
label?: string | ReactNode;
labelStyle?: CSSProperties;
labelShowBg?: boolean;
labelBgStyle?: CSSProperties;
labelBgPadding?: [number, number];
labelBgBorderRadius?: number;
style?: CSSProperties;
data?: T;
sourceHandleId?: string | null;
targetHandleId?: string | null;
markerStart?: string;
markerEnd?: string;
// @TODO: how can we get better types for pathOptions?
pathOptions?: any;
interactionWidth?: number;
};
export type BaseEdgeProps = Pick<
EdgeProps,
| 'label'
| 'labelStyle'
| 'labelShowBg'
| 'labelBgStyle'
| 'labelBgPadding'
| 'labelBgBorderRadius'
| 'style'
| 'markerStart'
| 'markerEnd'
| 'interactionWidth'
> & {
labelX: number;
labelY: number;
path: string;
};
export type EdgeMouseHandler = (event: React.MouseEvent, edge: Edge) => void;
export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void;
export type WrapEdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle'> & {
onClick?: EdgeMouseHandler;
@@ -129,30 +84,57 @@ export type WrapEdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandl
onMouseMove?: EdgeMouseHandler;
onMouseLeave?: EdgeMouseHandler;
edgeUpdaterRadius?: number;
onEdgeUpdateStart?: (event: React.MouseEvent, edge: Edge, handleType: HandleType) => void;
onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge, handleType: HandleType) => void;
onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge, handleType: HandleType) => void;
rfId?: string;
disableKeyboardA11y: boolean;
isFocusable: boolean;
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
};
export interface SmoothStepEdgeProps<T = any> extends EdgeProps<T> {
pathOptions?: 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 interface BezierEdgeProps<T = any> extends EdgeProps<T> {
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 interface EdgeTextProps extends HTMLAttributes<SVGElement> {
x: number;
y: number;
label?: string | ReactNode;
labelStyle?: CSSProperties;
labelShowBg?: boolean;
labelBgStyle?: CSSProperties;
labelBgPadding?: [number, number];
labelBgBorderRadius?: number;
}
};
export type EdgeTextProps = HTMLAttributes<SVGElement> &
EdgeLabelOptions & {
x: number;
y: number;
};
export enum ConnectionLineType {
Bezier = 'default',
@@ -179,7 +161,7 @@ export type ConnectionLineComponent = ComponentType<ConnectionLineComponentProps
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void;
export interface EdgeMarker {
export type EdgeMarker = {
type: MarkerType;
color?: string;
width?: number;
@@ -187,7 +169,7 @@ export interface EdgeMarker {
markerUnits?: string;
orient?: string;
strokeWidth?: number;
}
};
export type EdgeMarkerType = string | EdgeMarker;
+14 -11
View File
@@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { MouseEvent as ReactMouseEvent, ComponentType, MemoExoticComponent } from 'react';
import type { MouseEvent as ReactMouseEvent, ComponentType, MemoExoticComponent } from 'react';
import type { Selection as D3Selection, ZoomBehavior } from 'd3';
import { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
import { NodeChange, EdgeChange } from './changes';
import {
import type { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
import type { NodeChange, EdgeChange } from './changes';
import type {
Node,
NodeInternals,
NodeDimensionUpdate,
@@ -15,10 +15,10 @@ import {
SelectionDragHandler,
NodeOrigin,
} from './nodes';
import { Edge, EdgeProps, WrapEdgeProps } from './edges';
import { HandleType, StartHandle } from './handles';
import { DefaultEdgeOptions } from '.';
import { ReactFlowInstance } from './instance';
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>> };
@@ -115,7 +115,7 @@ export type UnselectNodesAndEdgesParams = {
export type OnViewportChange = (viewport: Viewport) => void;
export interface ViewportHelperFunctions {
export type ViewportHelperFunctions = {
zoomIn: ZoomInOut;
zoomOut: ZoomInOut;
zoomTo: ZoomTo;
@@ -127,7 +127,7 @@ export interface ViewportHelperFunctions {
fitBounds: FitBounds;
project: Project;
viewportInitialized: boolean;
}
};
export type ReactFlowStore = {
rfId: string;
@@ -167,6 +167,8 @@ export type ReactFlowStore = {
nodesDraggable: boolean;
nodesConnectable: boolean;
nodesFocusable: boolean;
edgesFocusable: boolean;
elementsSelectable: boolean;
multiSelectionActive: boolean;
@@ -222,6 +224,7 @@ export type ReactFlowActions = {
setMaxZoom: (maxZoom: number) => void;
setTranslateExtent: (translateExtent: CoordinateExtent) => void;
setNodeExtent: (nodeExtent: CoordinateExtent) => void;
cancelConnection: () => void;
reset: () => void;
};
@@ -239,6 +242,6 @@ 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;
account?: string;
hideAttribution: boolean;
};
+1 -2
View File
@@ -1,5 +1,4 @@
import { XYPosition, Position, Dimensions } from './utils';
import { OnConnect, Connection } from './general';
import type { XYPosition, Position, Dimensions, OnConnect, Connection } from '.';
export type HandleType = 'source' | 'target';
+1 -3
View File
@@ -1,8 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-namespace */
import { ViewportHelperFunctions, Viewport } from './general';
import { Node } from './nodes';
import { Edge } from './edges';
import { ViewportHelperFunctions, Viewport, Node, Edge } from '.';
export type ReactFlowJsonObject<NodeData = any, EdgeData = any> = {
nodes: Node<NodeData>[];
+44 -57
View File
@@ -1,20 +1,19 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
import { XYPosition, Position, CoordinateExtent } from './utils';
import { HandleElement } from './handles';
import { internalsSymbol } from '../utils';
import type { XYPosition, Position, CoordinateExtent, HandleElement } from '.';
// interface for the user node items
export interface Node<T = any> {
export type Node<T = any> = {
id: string;
position: XYPosition;
data: T;
type?: string;
style?: CSSProperties;
className?: string;
targetPosition?: Position;
sourcePosition?: Position;
targetPosition?: Position;
hidden?: boolean;
selected?: boolean;
dragging?: boolean;
@@ -31,6 +30,7 @@ export interface Node<T = any> {
expandParent?: boolean;
positionAbsolute?: XYPosition;
ariaLabel?: string;
focusable?: boolean;
// only used internally
[internalsSymbol]?: {
@@ -38,63 +38,50 @@ export interface Node<T = any> {
handleBounds?: NodeHandleBounds;
isParent?: boolean;
};
}
// props that get passed to a custom node
export interface NodeProps<T = any> {
id: string;
type: string;
data: T;
selected: boolean;
isConnectable: boolean;
xPos: number;
yPos: number;
dragging: boolean;
zIndex: number;
targetPosition?: Position;
sourcePosition?: Position;
dragHandle?: string;
}
};
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 interface WrapNodeProps<T = any> {
id: string;
type: string;
data: T;
selected: boolean;
isConnectable: boolean;
xPos: number;
yPos: number;
xPosOrigin: number;
yPosOrigin: number;
initialized: boolean;
isSelectable: boolean;
isDraggable: boolean;
selectNodesOnDrag: boolean;
onClick?: NodeMouseHandler;
onDoubleClick?: NodeMouseHandler;
onMouseEnter?: NodeMouseHandler;
onMouseMove?: NodeMouseHandler;
onMouseLeave?: NodeMouseHandler;
onContextMenu?: NodeMouseHandler;
style?: CSSProperties;
className?: string;
sourcePosition: Position;
targetPosition: Position;
hidden?: boolean;
resizeObserver: ResizeObserver | null;
dragHandle?: string;
zIndex: number;
isParent: boolean;
noDragClassName: string;
noPanClassName: string;
rfId: string;
disableKeyboardA11y: boolean;
ariaLabel?: string;
}
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;
+1 -1
View File
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Node, Edge, EdgeChange, NodeChange } from '../types';
import type { Node, Edge, EdgeChange, NodeChange } from '../types';
function handleParentExpand(res: any[], updateItem: any) {
const parent = res.find((e) => e.id === updateItem.parentNode);
+2 -2
View File
@@ -2,7 +2,7 @@
import type { Selection as D3Selection } from 'd3';
import { boxToRect, clamp, devWarn, getBoundsOfBoxes, rectToBox } from '../utils';
import { Node, Edge, Connection, EdgeMarkerType, Transform, XYPosition, Rect, NodeInternals } from '../types';
import type { Node, Edge, Connection, EdgeMarkerType, Transform, XYPosition, Rect, NodeInternals } from '../types';
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
'id' in element && 'source' in element && 'target' in element;
@@ -188,7 +188,7 @@ export const getNodesInside = (
const area = (width || 0) * (height || 0);
const isVisible = notInitialized || partiallyVisible || overlappingArea >= area;
if (isVisible) {
if (isVisible || node.dragging) {
visibleNodes.push(node);
}
});
+1 -1
View File
@@ -1,4 +1,4 @@
import { Dimensions, XYPosition, CoordinateExtent, Box, Rect } from '../types';
import type { Dimensions, XYPosition, CoordinateExtent, Box, Rect } from '../types';
export const getDimensions = (node: HTMLDivElement): Dimensions => ({
width: node.offsetWidth,