Merge branch 'main' into middlewares

This commit is contained in:
Moritz Klack
2025-12-02 16:34:21 +01:00
committed by GitHub
115 changed files with 2360 additions and 426 deletions
+6
View File
@@ -50,6 +50,7 @@ import CancelConnection from '../examples/CancelConnection';
import InteractiveMinimap from '../examples/InteractiveMinimap';
import UseOnSelectionChange from '../examples/UseOnSelectionChange';
import NodeToolbar from '../examples/NodeToolbar';
import EdgeToolbar from '../examples/EdgeToolbar';
import UseConnection from '../examples/UseConnection';
import UseNodesInitialized from '../examples/UseNodesInit';
import UseNodesData from '../examples/UseNodesData';
@@ -193,6 +194,11 @@ const routes: IRoute[] = [
path: 'edge-routing',
component: EdgeRouting,
},
{
name: 'Edge Toolbar',
path: 'edge-toolbar',
component: EdgeToolbar,
},
{
name: 'Empty',
path: 'empty',
+48 -38
View File
@@ -1,4 +1,4 @@
import { MouseEvent, useCallback } from 'react';
import { MouseEvent, useCallback, useState } from 'react';
import {
ReactFlow,
MiniMap,
@@ -134,47 +134,57 @@ const BasicFlow = () => {
});
fitView();
};
const [isHidden, setIsHidden] = useState(false);
const toggleVisibility = () => {
setIsHidden(!isHidden);
};
return (
<ReactFlow
defaultNodes={initialNodes}
defaultEdges={initialEdges}
onNodesChange={console.log}
onNodeClick={onNodeClick}
onNodeDragStop={onNodeDragStop}
onNodeDragStart={onNodeDragStart}
onNodeDrag={onNodeDrag}
onSelectionDragStart={printSelectionEvent('selection drag start')}
onSelectionDrag={printSelectionEvent('selection drag')}
onSelectionDragStop={printSelectionEvent('selection drag stop')}
className="react-flow-basic-example"
minZoom={0.2}
maxZoom={4}
fitView
fitViewOptions={fitViewOptions}
defaultEdgeOptions={defaultEdgeOptions}
selectNodesOnDrag={false}
elevateEdgesOnSelect
elevateNodesOnSelect={false}
nodeDragThreshold={0}
>
<Background variant={BackgroundVariant.Dots} />
<MiniMap />
<Controls />
<>
<ReactFlow
defaultNodes={initialNodes}
defaultEdges={initialEdges}
onNodesChange={console.log}
onNodeClick={onNodeClick}
onNodeDragStop={onNodeDragStop}
onNodeDragStart={onNodeDragStart}
onNodeDrag={onNodeDrag}
onSelectionDragStart={printSelectionEvent('selection drag start')}
onSelectionDrag={printSelectionEvent('selection drag')}
onSelectionDragStop={printSelectionEvent('selection drag stop')}
className="react-flow-basic-example"
style={{ display: isHidden ? 'none' : 'block' }}
minZoom={0.2}
maxZoom={4}
fitView
fitViewOptions={fitViewOptions}
defaultEdgeOptions={defaultEdgeOptions}
selectNodesOnDrag={false}
elevateEdgesOnSelect
elevateNodesOnSelect={false}
nodeDragThreshold={0}
>
<Background variant={BackgroundVariant.Dots} />
<MiniMap />
<Controls />
<Panel position="top-right">
<button onClick={resetTransform}>reset transform</button>
<button onClick={updatePos}>change pos</button>
<button onClick={toggleClassnames}>toggle classnames</button>
<button onClick={logToObject}>toObject</button>
<Panel position="top-right">
<button onClick={resetTransform}>reset transform</button>
<button onClick={updatePos}>change pos</button>
<button onClick={toggleClassnames}>toggle classnames</button>
<button onClick={logToObject}>toObject</button>
<button onClick={deleteSelectedElements}>deleteSelectedElements</button>
<button onClick={deleteSomeElements}>deleteSomeElements</button>
<button onClick={onSetNodes}>setNodes</button>
<button onClick={onUpdateNode}>updateNode</button>
<button onClick={addNode}>addNode</button>
</Panel>
</ReactFlow>
<button onClick={deleteSelectedElements}>deleteSelectedElements</button>
<button onClick={deleteSomeElements}>deleteSomeElements</button>
<button onClick={onSetNodes}>setNodes</button>
<button onClick={onUpdateNode}>updateNode</button>
<button onClick={addNode}>addNode</button>
</Panel>
</ReactFlow>
<button onClick={toggleVisibility} style={{ position: 'absolute', zIndex: 10, right: 10, top: 100 }}>
{isHidden ? 'Show' : 'Hide'} Flow
</button>
</>
);
};
@@ -1,6 +1,7 @@
import { ConnectionLineComponentProps } from '@xyflow/react';
function ConnectionLine({ fromX, fromY, toX, toY }: ConnectionLineComponentProps) {
function ConnectionLine({ fromX, fromY, toX, toY, pointer }: ConnectionLineComponentProps) {
console.log('pointer', pointer);
return (
<>
<path
@@ -1,4 +1,4 @@
import { MouseEvent, CSSProperties, useCallback } from 'react';
import { MouseEvent, CSSProperties, useCallback, useState } from 'react';
import {
ReactFlow,
@@ -14,19 +14,13 @@ import {
ReactFlowInstance,
useEdgesState,
useNodesState,
Panel,
} from '@xyflow/react';
const onInit = (reactFlowInstance: ReactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const buttonStyle: CSSProperties = {
position: 'absolute',
left: 10,
top: 10,
zIndex: 4,
};
const CustomMiniMapNode = ({ x, y, width, height }: MiniMapNodeProps) => {
return <circle cx={x} cy={y} r={Math.max(width, height) / 2} fill="#ffcc00" />;
};
@@ -34,6 +28,7 @@ const CustomMiniMapNode = ({ x, y, width, height }: MiniMapNodeProps) => {
const CustomMiniMapNodeFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]);
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
const [hideAllNodes, setHideAllNodes] = useState(false);
const onConnect = useCallback((params: Connection | Edge) => setEdges((els) => addEdge(params, els)), [setEdges]);
const addRandomNode = () => {
@@ -45,10 +40,20 @@ const CustomMiniMapNodeFlow = () => {
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
},
hidden: hideAllNodes,
};
setNodes((nds) => nds.concat(newNode));
};
const toggleHideAllNodes = () => {
setHideAllNodes(prev => {
const next = !prev;
setNodes(nds => nds.map(n => ({ ...n, hidden: next })));
setEdges(eds => eds.map(e => ({ ...e, hidden: next })));
return next;
});
};
return (
<ReactFlow
nodes={nodes}
@@ -65,9 +70,14 @@ const CustomMiniMapNodeFlow = () => {
<Background variant={BackgroundVariant.Lines} />
<MiniMap nodeComponent={CustomMiniMapNode} />
<button type="button" onClick={addRandomNode} style={buttonStyle}>
add node
</button>
<Panel position="top-left">
<button type="button" onClick={addRandomNode}>
add node
</button>
<button type="button" onClick={toggleHideAllNodes}>
{hideAllNodes ? 'show all nodes' : 'hide all nodes'}
</button>
</Panel>
</ReactFlow>
);
};
@@ -30,7 +30,7 @@ function ColorSelectorNode({ data, isConnectable }: NodeProps<ColorSelectorNode>
<div>
Custom Color Picker Node: <strong>{data.color}</strong>
</div>
<input className="nodrag" type="color" onChange={data.onChange} defaultValue={data.color} />
<input className="nodrag nokey" type="color" onChange={data.onChange} defaultValue={data.color} />
<Handle
type="source"
position={Position.Right}
@@ -0,0 +1,40 @@
import { getBezierPath, BaseEdge, EdgeProps, useReactFlow, getStraightPath, getSmoothStepPath } from '@xyflow/react';
import { EdgeToolbar } from '@xyflow/react';
const getPath = (props: EdgeProps) => {
switch (props.data!.type) {
case 'smoothstep':
return getSmoothStepPath(props);
case 'straight':
return getStraightPath(props);
default:
return getBezierPath(props);
}
};
export function CustomEdge(props: EdgeProps) {
const [edgePath, centerX, centerY] = getPath(props);
const { setEdges } = useReactFlow();
const deleteEdge = () => {
setEdges((edges) => edges.filter((edge) => edge.id !== props.id));
};
return (
<>
<BaseEdge id={props.id} path={edgePath} />
<EdgeToolbar
edgeId={props.id}
x={centerX + 0}
y={centerY + 0}
alignX={props.data?.align?.[0] ?? 'center'}
alignY={props.data?.align?.[1] ?? 'center'}
isVisible
>
<button style={{}} onClick={deleteEdge}>
Delete
</button>
</EdgeToolbar>
</>
);
}
@@ -0,0 +1,80 @@
import {
Background,
BackgroundVariant,
Controls,
Edge,
EdgeTypes,
MiniMap,
Node,
Position,
ReactFlow,
} from '@xyflow/react';
import { CustomEdge } from './CustomEdge';
const edgeTypes: EdgeTypes = {
custom: CustomEdge,
};
const initialNodes: Node[] = [
{
id: '1',
data: { label: 'Node 1', toolbarPosition: Position.Top },
position: { x: 0, y: 0 },
className: 'react-flow__node-default',
},
{
id: '2',
data: { label: 'Node 2', toolbarPosition: Position.Top },
position: { x: 100, y: 150 },
className: 'react-flow__node-default',
},
{
id: '3',
data: { label: 'Node 3', toolbarPosition: Position.Top },
position: { x: 200, y: 0 },
className: 'react-flow__node-default',
},
];
const initialEdges: Edge[] = [
{
id: 'e1-2',
source: '1',
target: '2',
type: 'custom',
data: { type: 'smoothstep', align: ['left', 'bottom'] },
},
{
id: 'e3-2',
source: '3',
target: '2',
type: 'custom',
data: { type: 'bezier', align: ['right', 'bottom'] },
},
{
id: 'e1-3',
source: '1',
target: '3',
type: 'custom',
data: { type: 'straight', align: ['center', 'center'] },
},
];
export default function EdgeToolbarExample() {
return (
<ReactFlow
defaultNodes={initialNodes}
defaultEdges={initialEdges}
className="react-flow-edge-toolbar-example"
minZoom={0.2}
maxZoom={4}
fitView
edgeTypes={edgeTypes}
>
<Background variant={BackgroundVariant.Dots} />
<MiniMap />
<Controls />
</ReactFlow>
);
}
+8 -1
View File
@@ -30,10 +30,11 @@ const BasicFlow = () => {
<ReactFlow
defaultNodes={initialNodes}
defaultEdges={initialEdges}
selectionOnDrag
selectionOnDrag={true}
selectionMode={SelectionMode.Partial}
panOnDrag={panOnDrag}
panOnScroll
paneClickDistance={100}
zoomActivationKeyCode="Meta"
multiSelectionKeyCode={MULTI_SELECT_KEY}
onPaneContextMenu={onPaneContextMenu}
@@ -43,6 +44,12 @@ const BasicFlow = () => {
onMoveStart={onMoveStart}
onMove={onMove}
onMoveEnd={onMoveEnd}
onPaneClick={(e) => console.log('pane click', e)}
onSelectionStart={(e) => console.log('on selection start', e)}
onSelectionEnd={(e) => console.log('on selection end', e)}
onPointerDown={(e) => console.log('pointer down', e)}
onPointerUp={(e) => console.log('pointer up', e)}
onClick={(e) => console.log('click', e)}
>
<Background variant={BackgroundVariant.Cross} />
<Controls />
@@ -1,7 +1,9 @@
import { memo, FC } from 'react';
import { Handle, Position, NodeProps, NodeResizer } from '@xyflow/react';
import { Handle, Position, NodeProps, NodeResizer, useKeyPress } from '@xyflow/react';
const DefaultResizerNode: FC<NodeProps> = ({ data, selected }) => {
const keepAspectRatio = useKeyPress('k');
return (
<>
<NodeResizer
@@ -14,7 +16,7 @@ const DefaultResizerNode: FC<NodeProps> = ({ data, selected }) => {
onResizeStart={data.onResizeStart ?? undefined}
onResize={data.onResize ?? undefined}
onResizeEnd={data.onResizeEnd ?? undefined}
keepAspectRatio={data.keepAspectRatio ?? undefined}
keepAspectRatio={keepAspectRatio || (data.keepAspectRatio ?? undefined)}
/>
<Handle type="target" position={Position.Left} />
<div>{data.label}</div>
@@ -239,6 +239,16 @@ const StressFlow = () => {
<button onClick={remount}>re-mount</button>
<button onClick={updatePos}>change pos</button>
<button onClick={updateElements}>update elements</button>
<button
onClick={() => {
setNodes((nds) => [
...nds,
{ id: (nds.length + 1).toString(), position: { x: 0, y: 0 }, data: { label: `Node ${nds.length + 1}` } },
]);
}}
>
Add element
</button>
</Panel>
</ReactFlow>
);
+11 -11
View File
@@ -27,15 +27,15 @@ const onEdgeClick = (_: MouseEvent, edge: Edge) => console.log('click', edge);
const defaultViewport = { x: 0, y: 0, zoom: 1.5 };
const initialNodes: Node[] = [
{
id: 'extent',
position: { x: 0, y: 0 },
width: 1000,
height: 1000,
data: { label: 'Extent' },
origin: [0, 0],
zIndex: -1,
},
// {
// id: 'extent',
// position: { x: 0, y: 0 },
// width: 1000,
// height: 1000,
// data: { label: 'Extent' },
// origin: [0, 0],
// zIndex: -1,
// },
{
id: '1',
type: 'input',
@@ -96,12 +96,12 @@ const initialNodes: Node[] = [
},
{
id: '5',
type: 'group',
// type: 'group',
data: { label: 'Node 5' },
position: { x: 650, y: 250 },
className: 'light',
style: { width: 100, height: 100 },
zIndex: 1000,
// zIndex: 1000,
},
{
id: '5a',