Skip to content

Commit

Permalink
fix:优化添加节点按钮和连线按钮冲突的问题,连线更丝滑
Browse files Browse the repository at this point in the history
  • Loading branch information
昔梦 committed Nov 29, 2024
1 parent 4d3e944 commit 673e61f
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 55 deletions.
15 changes: 11 additions & 4 deletions packages/x-flow/src/components/CustomNode/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
}

.react-flow__handle {
width: 32px;
height: 32px;
width: 30px;
height: 30px;
background: transparent;
border-radius: 0;
border: none;
}

.react-flow__handle::after {
content: '';
--tw-bg-opacity: 1;
Expand All @@ -26,6 +26,12 @@
margin: 11px 0 8px 15px;
}

.react-flow__handle:hover {
.xflow-node-add-box {
scale: 130%;
}
}

.xflow-node-add-box {
position: absolute;
top: 8px;
Expand All @@ -37,6 +43,8 @@
align-items: center;
justify-content: center;
background-color: #2970ff;
pointer-events: none;
transition: all 0.3s;
}
}

Expand All @@ -59,4 +67,3 @@
display: none;
}
}

89 changes: 52 additions & 37 deletions packages/x-flow/src/components/CustomNode/index.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,61 @@
import React, { memo, useContext, useState } from 'react';
import { Tooltip } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { Handle, Position, useReactFlow } from '@xyflow/react';
import { Tooltip } from 'antd';
import classNames from 'classnames';
import produce from 'immer';
import React, { memo, useContext, useRef, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { Handle, Position, useReactFlow } from '@xyflow/react';
import { capitalize, uuid } from '../../utils';
import useStore from '../../models/store';
import { ConfigContext } from '../../models/context';
import useStore from '../../models/store';
import { capitalize, uuid } from '../../utils';
import NodeSelectPopover from '../NodesPopover';
import './index.less';

export default memo((props: any) => {
const { id, type, data, layout, isConnectable, selected, onClick } = props;
const { widgets, settingMap } = useContext(ConfigContext);
const NodeWidget = widgets[`${capitalize(type)}Node`] || widgets['CommonNode'];

const NodeWidget =
widgets[`${capitalize(type)}Node`] || widgets['CommonNode'];
const [isHovered, setIsHovered] = useState(false);
const [isShowTooltip, setIsShowTooltip] = useState(false);
const popoverRef = useRef(null);
const [openNodeSelectPopover, setOpenNodeSelectPopover] = useState(false);

const reactflow = useReactFlow();
const {
edges,
nodes,
setNodes,
setEdges,
mousePosition,
} = useStore(
const { edges, nodes, setNodes, setEdges, mousePosition } = useStore(
useShallow((state: any) => ({
nodes: state.nodes,
edges: state.edges,
mousePosition: state.mousePosition,
setNodes: state.setNodes,
setEdges: state.setEdges,
onEdgesChange: state.onEdgesChange
onEdgesChange: state.onEdgesChange,
}))
);

// 增加节点并进行联系
const handleAddNode = (data: any) => {
const { screenToFlowPosition } = reactflow;
const { x, y } = screenToFlowPosition({ x: mousePosition.pageX + 100, y: mousePosition.pageY + 100 });
const { x, y } = screenToFlowPosition({
x: mousePosition.pageX + 100,
y: mousePosition.pageY + 100,
});
const targetId = uuid();

const newNodes = produce(nodes, (draft: any) => {
draft.push({
id: targetId,
type: 'custom',
data,
position: { x, y }
position: { x, y },
});
});
const newEdges = produce(edges, (draft: any) => {
draft.push({
id: uuid(),
source: id,
target: targetId,
})
});
});
setNodes(newNodes);
setEdges(newEdges);
Expand All @@ -71,14 +72,14 @@ export default memo((props: any) => {
<div
className={classNames('xflow-node-container', {
['xflow-node-container-selected']: !!selected,
['xflow-node-container-tb']: layout === 'TB'
['xflow-node-container-tb']: layout === 'TB',
})}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{!settingMap?.[type]?.targetHandleHidden && (
<Handle
type='target'
type="target"
position={targetPosition}
isConnectable={isConnectable}
/>
Expand All @@ -91,29 +92,43 @@ export default memo((props: any) => {
/>
{!settingMap?.[type]?.sourceHandleHidden && (
<Handle
type='source'
type="source"
position={sourcePosition}
isConnectable={isConnectable}
onMouseEnter={() => setIsShowTooltip(true)}
onMouseLeave={() => setIsShowTooltip(false)}
onClick={e => {
e.stopPropagation();
popoverRef?.current?.changeOpen(true);
setIsShowTooltip(false);
setOpenNodeSelectPopover(true);
}}
>
{(selected || isHovered) && (
<div className='xflow-node-add-box'>
<NodeSelectPopover placement='right' addNode={handleAddNode}>
<Tooltip
title='点击添加节点'
arrow={false}
overlayInnerStyle={{
background: '#fff',
color: '#354052',
fontSize: '12px'
}}
>
<PlusOutlined style={{ color: '#fff', fontSize: 10 }}/>
</Tooltip>
</NodeSelectPopover>
{(selected || isHovered || openNodeSelectPopover) && (
<div className="xflow-node-add-box">
<NodeSelectPopover
placement="right"
addNode={handleAddNode}
ref={popoverRef}
onNodeSelectPopoverChange={(val)=>setOpenNodeSelectPopover(val)}
>
<Tooltip
title="点击添加节点"
arrow={false}
overlayInnerStyle={{
background: '#fff',
color: '#354052',
fontSize: '12px',
}}
open={isShowTooltip}
>
<PlusOutlined style={{ color: '#fff', fontSize: 10 }} />
</Tooltip>
</NodeSelectPopover>
</div>
)}
</Handle>
)}
</div>
);
})
});
41 changes: 27 additions & 14 deletions packages/x-flow/src/components/NodesPopover/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,45 @@

import React, { useCallback, useState, useRef, useContext } from 'react';
import { Popover } from 'antd';
import { useShallow } from 'zustand/react/shallow';
import { useClickAway } from 'ahooks';
import useStore from '../../models/store';
import { Popover } from 'antd';
import React, {
forwardRef,
useCallback,
useContext,
useImperativeHandle,
useRef,
useState,
} from 'react';
import { ConfigContext } from '../../models/context';
import NodesMenu from '../NodesMenu';

export default (props: any) => {
const { addNode, children } = props;
export default forwardRef((props: any, popoverRef) => {
const { addNode, children, onNodeSelectPopoverChange } = props;

const ref = useRef<any>(null);
const closeRef: any = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(false);

const { settings, nodeSelector } = useContext(ConfigContext);
const { showSearch, popoverProps = { placement: 'top' } } = nodeSelector || {};
const { showSearch, popoverProps = { placement: 'top' } } =
nodeSelector || {};

useImperativeHandle(popoverRef, () => ({
changeOpen: val => {
setOpen(val);
},
}));

useClickAway(() => {
if (closeRef.current) {
setOpen(false);
onNodeSelectPopoverChange && onNodeSelectPopoverChange(false);
closeRef.current = false;
}
}, ref);

const handCreateNode = useCallback<any>(({ type }) => {
addNode({ _nodeType: type });
setOpen(false);
onNodeSelectPopoverChange && onNodeSelectPopoverChange(false);
}, []);

return (
Expand All @@ -35,24 +48,24 @@ export default (props: any) => {
arrow={false}
overlayInnerStyle={{ padding: '12px 6px' }}
{...popoverProps}
trigger='click'
trigger="click"
open={open}
onOpenChange={() => {
setTimeout(() => {
closeRef.current = true;
setOpen(true);
}, 50)
}, 50);
}}
content={(
content={
<NodesMenu
ref={ref}
items={settings}
showSearch={showSearch}
onClick={handCreateNode}
onClick={handCreateNode}
/>
)}
}
>
{children}
</Popover>
);
}
});

0 comments on commit 673e61f

Please sign in to comment.