mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? feat: add FlowHeader and delete edge #918 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
import { useCallback, useRef, useState } from 'react';
|
|
import { NodeMouseHandler, useReactFlow } from 'reactflow';
|
|
|
|
import styles from './index.less';
|
|
|
|
export interface INodeContextMenu {
|
|
id: string;
|
|
top: number;
|
|
left: number;
|
|
right?: number;
|
|
bottom?: number;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
export function NodeContextMenu({
|
|
id,
|
|
top,
|
|
left,
|
|
right,
|
|
bottom,
|
|
...props
|
|
}: INodeContextMenu) {
|
|
const { getNode, setNodes, addNodes, setEdges } = useReactFlow();
|
|
|
|
const duplicateNode = useCallback(() => {
|
|
const node = getNode(id);
|
|
const position = {
|
|
x: node?.position?.x || 0 + 50,
|
|
y: node?.position?.y || 0 + 50,
|
|
};
|
|
|
|
addNodes({
|
|
...(node || {}),
|
|
data: node?.data,
|
|
selected: false,
|
|
dragging: false,
|
|
id: `${node?.id}-copy`,
|
|
position,
|
|
});
|
|
}, [id, getNode, addNodes]);
|
|
|
|
const deleteNode = useCallback(() => {
|
|
setNodes((nodes) => nodes.filter((node) => node.id !== id));
|
|
setEdges((edges) => edges.filter((edge) => edge.source !== id));
|
|
}, [id, setNodes, setEdges]);
|
|
|
|
return (
|
|
<div
|
|
style={{ top, left, right, bottom }}
|
|
className={styles.contextMenu}
|
|
{...props}
|
|
>
|
|
<p style={{ margin: '0.5em' }}>
|
|
<small>node: {id}</small>
|
|
</p>
|
|
<button onClick={duplicateNode} type={'button'}>
|
|
duplicate
|
|
</button>
|
|
<button onClick={deleteNode} type={'button'}>
|
|
delete
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export const useHandleNodeContextMenu = (sideWidth: number) => {
|
|
const [menu, setMenu] = useState<INodeContextMenu>({} as INodeContextMenu);
|
|
const ref = useRef<any>(null);
|
|
|
|
const onNodeContextMenu: NodeMouseHandler = useCallback(
|
|
(event, node) => {
|
|
// Prevent native context menu from showing
|
|
event.preventDefault();
|
|
|
|
// Calculate position of the context menu. We want to make sure it
|
|
// doesn't get positioned off-screen.
|
|
const pane = ref.current?.getBoundingClientRect();
|
|
// setMenu({
|
|
// id: node.id,
|
|
// top: event.clientY < pane.height - 200 ? event.clientY : 0,
|
|
// left: event.clientX < pane.width - 200 ? event.clientX : 0,
|
|
// right: event.clientX >= pane.width - 200 ? pane.width - event.clientX : 0,
|
|
// bottom:
|
|
// event.clientY >= pane.height - 200 ? pane.height - event.clientY : 0,
|
|
// });
|
|
|
|
setMenu({
|
|
id: node.id,
|
|
top: event.clientY - 72,
|
|
left: event.clientX - sideWidth,
|
|
// top: event.clientY < pane.height - 200 ? event.clientY - 72 : 0,
|
|
// left: event.clientX < pane.width - 200 ? event.clientX : 0,
|
|
});
|
|
},
|
|
[sideWidth],
|
|
);
|
|
|
|
// Close the context menu if it's open whenever the window is clicked.
|
|
const onPaneClick = useCallback(
|
|
() => setMenu({} as INodeContextMenu),
|
|
[setMenu],
|
|
);
|
|
|
|
return { onNodeContextMenu, menu, onPaneClick, ref };
|
|
};
|