|
|
|
|
@ -12,7 +12,11 @@ import FlowEditorMain from './FlowEditorMain';
|
|
|
|
|
import { useFlowEditorState } from '@/hooks/useFlowEditorState';
|
|
|
|
|
import { useFlowCallbacks } from '@/hooks/useFlowCallbacks';
|
|
|
|
|
|
|
|
|
|
const FlowEditorWithProvider: React.FC<{ initialData?: any, useDefault?: boolean, readOnly?: boolean }> = ({ initialData, useDefault, readOnly }) => {
|
|
|
|
|
const FlowEditorWithProvider: React.FC<{
|
|
|
|
|
initialData?: any,
|
|
|
|
|
useDefault?: boolean,
|
|
|
|
|
readOnly?: boolean
|
|
|
|
|
}> = ({ initialData, useDefault, readOnly }) => {
|
|
|
|
|
return (
|
|
|
|
|
<div style={{ width: '100%', height: '91vh', display: 'flex' }} onContextMenu={(e) => e.preventDefault()}>
|
|
|
|
|
<ReactFlowProvider>
|
|
|
|
|
@ -22,7 +26,11 @@ const FlowEditorWithProvider: React.FC<{ initialData?: any, useDefault?: boolean
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean, readOnly?: boolean }> = ({ initialData, useDefault, readOnly = false }) => {
|
|
|
|
|
const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean, readOnly?: boolean }> = ({
|
|
|
|
|
initialData,
|
|
|
|
|
useDefault,
|
|
|
|
|
readOnly = false
|
|
|
|
|
}) => {
|
|
|
|
|
const reactFlowInstance = useReactFlow();
|
|
|
|
|
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
|
|
|
|
const [menu, setMenu] = useState<{
|
|
|
|
|
@ -205,30 +213,56 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean, readOnly?:
|
|
|
|
|
setMenu(null);
|
|
|
|
|
}, [setMenu]);
|
|
|
|
|
|
|
|
|
|
// 添加处理粘贴事件的函数
|
|
|
|
|
// 存储最后的鼠标位置
|
|
|
|
|
const lastMousePositionRef = useRef<{ x: number; y: number } | null>(null);
|
|
|
|
|
|
|
|
|
|
// 跟踪鼠标位置
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const handleMouseMove = (event: MouseEvent) => {
|
|
|
|
|
const pane = reactFlowWrapper.current?.getBoundingClientRect();
|
|
|
|
|
if (pane && reactFlowInstance) {
|
|
|
|
|
const position = reactFlowInstance.screenToFlowPosition({
|
|
|
|
|
x: event.clientX,
|
|
|
|
|
y: event.clientY
|
|
|
|
|
});
|
|
|
|
|
lastMousePositionRef.current = position;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const canvasElement = reactFlowWrapper.current;
|
|
|
|
|
if (canvasElement) {
|
|
|
|
|
canvasElement.addEventListener('mousemove', handleMouseMove);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
if (canvasElement) {
|
|
|
|
|
canvasElement.removeEventListener('mousemove', handleMouseMove);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}, [reactFlowInstance]);
|
|
|
|
|
|
|
|
|
|
// 处理粘贴事件
|
|
|
|
|
const handlePasteNode = useCallback((event: ClipboardEvent) => {
|
|
|
|
|
// 检查是否是粘贴操作且在画布区域内
|
|
|
|
|
// 右键菜单触发的粘贴(有menu.position)
|
|
|
|
|
if (event.clipboardData?.getData('text') === 'paste-node' && menu?.type === 'pane' && menu.position) {
|
|
|
|
|
pasteNode(menu.position);
|
|
|
|
|
setMenu(null);
|
|
|
|
|
}
|
|
|
|
|
}, [menu, pasteNode]);
|
|
|
|
|
}, [menu, pasteNode, setMenu]);
|
|
|
|
|
|
|
|
|
|
// 添加键盘事件监听
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
|
|
|
// 检查是否按下了 Ctrl+V (Windows/Linux) 或 Cmd+V (Mac)
|
|
|
|
|
if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
|
|
|
|
|
// 触发自定义粘贴事件
|
|
|
|
|
const pasteEvent = new ClipboardEvent('paste', {
|
|
|
|
|
clipboardData: new DataTransfer()
|
|
|
|
|
});
|
|
|
|
|
pasteEvent.clipboardData?.setData('text', 'paste-node');
|
|
|
|
|
document.dispatchEvent(pasteEvent);
|
|
|
|
|
// 键盘快捷键粘贴,使用最后的鼠标位置
|
|
|
|
|
if (lastMousePositionRef.current) {
|
|
|
|
|
pasteNode(lastMousePositionRef.current);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 为画布添加键盘事件监听
|
|
|
|
|
// 为画布添加键盘和粘贴事件监听
|
|
|
|
|
const canvasElement = reactFlowWrapper.current;
|
|
|
|
|
if (canvasElement) {
|
|
|
|
|
canvasElement.addEventListener('keydown', handleKeyDown);
|
|
|
|
|
@ -241,7 +275,7 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean, readOnly?:
|
|
|
|
|
}
|
|
|
|
|
document.removeEventListener('paste', handlePasteNode);
|
|
|
|
|
};
|
|
|
|
|
}, [handlePasteNode]);
|
|
|
|
|
}, [pasteNode, handlePasteNode]);
|
|
|
|
|
|
|
|
|
|
// 监听边的变化,处理添加节点的触发
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
@ -281,8 +315,8 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean, readOnly?:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
ref={reactFlowWrapper}
|
|
|
|
|
<div
|
|
|
|
|
ref={reactFlowWrapper}
|
|
|
|
|
style={{ width: '100%', height: '100%' }}
|
|
|
|
|
onContextMenu={(e) => e.preventDefault()}
|
|
|
|
|
tabIndex={0} // 使div可获得焦点以接收键盘事件
|
|
|
|
|
|