Merge branch 'main' into feat/hooks
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
name: '🐛 Bug report'
|
name: '🐛 Bug Report'
|
||||||
description: Create a report to help us improve React Flow
|
description: Create a report to help us improve React Flow
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
name: 🛠 Feature Request
|
||||||
|
description: Suggest a new feature or idea for React Flow
|
||||||
|
labels: feature request
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Please describe the feature that you want to propose
|
||||||
|
description: A clear and concise description of what you want to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
@@ -26,7 +26,6 @@ const initBgColor = '#1A192B';
|
|||||||
|
|
||||||
const connectionLineStyle = { stroke: '#fff' };
|
const connectionLineStyle = { stroke: '#fff' };
|
||||||
const snapGrid: SnapGrid = [16, 16];
|
const snapGrid: SnapGrid = [16, 16];
|
||||||
const defaultViewport = { x: 0, y: 0, zoom: 1.5 };
|
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
selectorNode: ColorSelectorNode,
|
selectorNode: ColorSelectorNode,
|
||||||
@@ -137,8 +136,9 @@ const CustomNodeFlow = () => {
|
|||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
snapToGrid={true}
|
snapToGrid={true}
|
||||||
snapGrid={snapGrid}
|
snapGrid={snapGrid}
|
||||||
defaultViewport={defaultViewport}
|
|
||||||
fitView
|
fitView
|
||||||
|
minZoom={0.3}
|
||||||
|
maxZoom={2}
|
||||||
>
|
>
|
||||||
<MiniMap
|
<MiniMap
|
||||||
nodeStrokeColor={(n: Node): string => {
|
nodeStrokeColor={(n: Node): string => {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
const logNodes = useCallback(() => {
|
const logNodes = useCallback(() => {
|
||||||
console.log('nodes', getNodes());
|
console.log('nodes', getNodes());
|
||||||
console.log('edges', getEdges());
|
console.log('edges', getEdges());
|
||||||
}, [getNodes]);
|
}, [getNodes, getEdges]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
addEdges({ id: 'e3-4', source: '3', target: '4' });
|
addEdges({ id: 'e3-4', source: '3', target: '4' });
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
import React, { memo, FC, useMemo, CSSProperties } from 'react';
|
import React, { useState, memo, FC, useMemo, CSSProperties } from 'react';
|
||||||
import { Handle, Position, NodeProps } from '@react-flow/bundle';
|
import { Handle, Position, NodeProps, useUpdateNodeInternals } from '@react-flow/bundle';
|
||||||
|
|
||||||
const nodeStyles: CSSProperties = { padding: 10, border: '1px solid #ddd' };
|
const nodeStyles: CSSProperties = { padding: 10, border: '1px solid #ddd' };
|
||||||
|
|
||||||
const CustomNode: FC<NodeProps> = ({ data }) => {
|
const CustomNode: FC<NodeProps> = ({ id }) => {
|
||||||
|
const [handleCount, setHandleCount] = useState(1);
|
||||||
|
const updateNodeInternals = useUpdateNodeInternals();
|
||||||
|
|
||||||
const handles = useMemo(
|
const handles = useMemo(
|
||||||
() =>
|
() =>
|
||||||
Array.from({ length: data.handleCount }, (x, i) => {
|
Array.from({ length: handleCount }, (x, i) => {
|
||||||
const handleId = `handle-${i}`;
|
const handleId = `handle-${i}`;
|
||||||
return (
|
return <Handle key={handleId} type="source" position={Position.Right} id={handleId} style={{ top: 10 * i }} />;
|
||||||
<Handle
|
|
||||||
key={handleId}
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
id={handleId}
|
|
||||||
style={{ top: 10 * i + data.handlePosition * 10 }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
[data.handleCount, data.handlePosition]
|
[handleCount]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={nodeStyles}>
|
<div style={nodeStyles}>
|
||||||
<Handle type="target" position={Position.Left} />
|
<Handle type="target" position={Position.Left} />
|
||||||
<div>output handle count: {data.handleCount}</div>
|
<div>output handle count: {handleCount}</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setHandleCount((c) => c + 1);
|
||||||
|
updateNodeInternals(id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
add handle
|
||||||
|
</button>
|
||||||
{handles}
|
{handles}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, CSSProperties, MouseEvent } from 'react';
|
import { useCallback, MouseEvent } from 'react';
|
||||||
import {
|
import {
|
||||||
ReactFlow,
|
ReactFlow,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
Node,
|
Node,
|
||||||
Connection,
|
Connection,
|
||||||
Edge,
|
Edge,
|
||||||
useUpdateNodeInternals,
|
|
||||||
Position,
|
Position,
|
||||||
useNodesState,
|
useNodesState,
|
||||||
useEdgesState,
|
useEdgesState,
|
||||||
@@ -16,28 +15,15 @@ import {
|
|||||||
|
|
||||||
import CustomNode from './CustomNode';
|
import CustomNode from './CustomNode';
|
||||||
|
|
||||||
const initialHandleCount = 1;
|
|
||||||
|
|
||||||
const initialNodes: Node[] = [
|
const initialNodes: Node[] = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
data: {
|
data: { label: 'Node 1' },
|
||||||
label: 'Node 1',
|
|
||||||
handleCount: initialHandleCount,
|
|
||||||
handlePosition: 0,
|
|
||||||
},
|
|
||||||
position: { x: 250, y: 5 },
|
position: { x: 250, y: 5 },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const buttonWrapperStyles: CSSProperties = {
|
|
||||||
position: 'absolute',
|
|
||||||
right: 10,
|
|
||||||
top: 10,
|
|
||||||
zIndex: 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodeTypes: NodeTypes = {
|
const nodeTypes: NodeTypes = {
|
||||||
custom: CustomNode,
|
custom: CustomNode,
|
||||||
};
|
};
|
||||||
@@ -50,7 +36,6 @@ const UpdateNodeInternalsFlow = () => {
|
|||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
||||||
|
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
|
||||||
const { project } = useReactFlow();
|
const { project } = useReactFlow();
|
||||||
|
|
||||||
const onPaneClick = useCallback(
|
const onPaneClick = useCallback(
|
||||||
@@ -67,36 +52,6 @@ const UpdateNodeInternalsFlow = () => {
|
|||||||
[project, setNodes]
|
[project, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleHandleCount = useCallback(() => {
|
|
||||||
setNodes((nds) =>
|
|
||||||
nds.map((node) => {
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
data: {
|
|
||||||
...node.data,
|
|
||||||
handleCount: node.data?.handleCount === 1 ? 2 : 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, [setNodes]);
|
|
||||||
|
|
||||||
const toggleHandlePosition = useCallback(() => {
|
|
||||||
setNodes((nds) =>
|
|
||||||
nds.map((node) => {
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
data: {
|
|
||||||
...node.data,
|
|
||||||
handlePosition: node.data?.handlePosition === 0 ? 1 : 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, [setNodes]);
|
|
||||||
|
|
||||||
const updateNode = useCallback(() => updateNodeInternals('1'), [updateNodeInternals]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
@@ -106,13 +61,7 @@ const UpdateNodeInternalsFlow = () => {
|
|||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onPaneClick={onPaneClick}
|
onPaneClick={onPaneClick}
|
||||||
>
|
/>
|
||||||
<div style={buttonWrapperStyles}>
|
|
||||||
<button onClick={toggleHandleCount}>toggle handle count</button>
|
|
||||||
<button onClick={toggleHandlePosition}>toggle handle position</button>
|
|
||||||
<button onClick={updateNode}>update node internals</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import useViewportHelper from './useViewportHelper';
|
import useViewportHelper from './useViewportHelper';
|
||||||
import { useStoreApi } from '../hooks/useStore';
|
import { useStoreApi } from '../hooks/useStore';
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
|
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
|
||||||
const { initialized: viewportInitialized, ...viewportHelperFunctions } = useViewportHelper();
|
const viewportHelper = useViewportHelper();
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
|
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
|
||||||
@@ -111,17 +111,18 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return useMemo(() => {
|
||||||
...viewportHelperFunctions,
|
return {
|
||||||
viewportInitialized,
|
...viewportHelper,
|
||||||
getNodes,
|
getNodes,
|
||||||
getNode,
|
getNode,
|
||||||
getEdges,
|
getEdges,
|
||||||
getEdge,
|
getEdge,
|
||||||
setNodes,
|
setNodes,
|
||||||
setEdges,
|
setEdges,
|
||||||
addNodes,
|
addNodes,
|
||||||
addEdges,
|
addEdges,
|
||||||
toObject,
|
toObject,
|
||||||
};
|
};
|
||||||
|
}, [viewportHelper, getNodes, getNode, getEdges, getEdge, setNodes, setEdges, addNodes, addEdges, toObject]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,10 @@ function useUpdateNodeInternals(): UpdateNodeInternals {
|
|||||||
const updateNodeDimensions = useStore(selector);
|
const updateNodeDimensions = useStore(selector);
|
||||||
|
|
||||||
return useCallback<UpdateNodeInternals>((id: string) => {
|
return useCallback<UpdateNodeInternals>((id: string) => {
|
||||||
const { domNode } = store.getState();
|
const nodeElement = store.getState().domNode?.querySelector(`.react-flow__node[data-id="${id}"]`) as HTMLDivElement;
|
||||||
if (!domNode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeElement = domNode.querySelector(`.react-flow__node[data-id="${id}"]`) as HTMLDivElement;
|
|
||||||
|
|
||||||
if (nodeElement) {
|
if (nodeElement) {
|
||||||
updateNodeDimensions([{ id, nodeElement, forceUpdate: true }]);
|
requestAnimationFrame(() => updateNodeDimensions([{ id, nodeElement, forceUpdate: true }]));
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const initialViewportHelper: ViewportHelperFunctions = {
|
|||||||
setCenter: noop,
|
setCenter: noop,
|
||||||
fitBounds: noop,
|
fitBounds: noop,
|
||||||
project: (position: XYPosition) => position,
|
project: (position: XYPosition) => position,
|
||||||
initialized: false,
|
viewportInitialized: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
@@ -72,7 +72,7 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
const { transform, snapToGrid, snapGrid } = store.getState();
|
const { transform, snapToGrid, snapGrid } = store.getState();
|
||||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
||||||
},
|
},
|
||||||
initialized: true,
|
viewportInitialized: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export interface ViewportHelperFunctions {
|
|||||||
setCenter: SetCenter;
|
setCenter: SetCenter;
|
||||||
fitBounds: FitBounds;
|
fitBounds: FitBounds;
|
||||||
project: Project;
|
project: Project;
|
||||||
initialized: boolean;
|
viewportInitialized: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReactFlowStore = {
|
export type ReactFlowStore = {
|
||||||
|
|||||||
Reference in New Issue
Block a user