This commit is contained in:
2024-09-18 22:46:14 +07:00
parent f1f47c9e30
commit 6700e9514e
9 changed files with 793 additions and 77 deletions

View File

@@ -5,16 +5,18 @@ import {
Controls,
Background,
BackgroundVariant,
SelectionMode
SelectionMode,
applyNodeChanges,
applyEdgeChanges,
MarkerType
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { FiUser, FiMail, FiCheckCircle, FiList } from 'react-icons/fi';
import { FaShuffle } from 'react-icons/fa6';
import '../assets/css/style.css';
import Tabbar from './Tabbar.tsx';
import ShowPopup from './ShowPopup.tsx';
import { nanoid } from 'nanoid';
import CustomEdge from './CustomEdge';
import MenuFlow from './MenuFlow';
const initialNodes = [
{
@@ -26,7 +28,7 @@ const initialNodes = [
<div className='step'>
<div className='left'>
<img
src='/src/assets/icons/icon-user.svg'
src='/src/assets/icons/icon-time.svg'
width='24px'
height='24px'
alt=''
@@ -40,8 +42,16 @@ const initialNodes = [
},
{
id: '2',
data: { label: 'Add Note' },
position: { x: 250, y: 250 }
key: 'add-node',
type: 'output',
data: {
label: (
<div className='add-node'>
<i>+</i> <span>Add a journey point</span>
</div>
)
},
position: { x: 250, y: 220 }
}
];
@@ -78,11 +88,17 @@ const nodeContents = {
`
};
const connectEdges = [
{
id: 'e1-2',
source: '1',
target: '2'
}
];
const NoteFlow = () => {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState([
{ id: 'e1-2', source: '1', target: '2' } // Kết nối node "Add Note" với node mặc định ban đầu
]);
const [edges, setEdges] = useState(connectEdges);
const [showTabBar, setShowTabBar] = useState(false);
const [lastNodeId, setLastNodeId] = useState('1');
const [nodeToDelete, setNodeToDelete] = useState(null);
@@ -99,13 +115,14 @@ const NoteFlow = () => {
});
};
const closeTabbar = () => {
console.log('aaa');
setIsTabbarVisible(false); // Ẩn tabbar khi click close
};
const onNodeClick = (event, node) => {
if (node.id === '2') {
// Check if the clicked node is "Add Note"
console.log(node);
if (node.key === 'add-node') {
console.log(node);
// Check if the clicked node is "Add Node"
setShowTabBar(true);
setIsTabbarVisible(true);
} else {
@@ -165,14 +182,13 @@ const NoteFlow = () => {
const addNode = (option) => {
const newNodeId = nanoid(8); // Generate a unique ID for the new node
const addNoteNode = nodes.find((node) => node.id === '2');
const initialX = addNoteNode.position.x;
console.log(initialX);
const addNoteNode = nodes.find((node) => node.key === 'add-node');
const initialX = addNoteNode?.position.x;
const nextY =
nodes.length > 2
? getNextYPosition(initialX, 130)
: addNoteNode.position.y;
? getNextYPosition(initialX, 120)
: addNoteNode?.position.y;
// Remove existing edges from addNoteNode
setEdges((eds) =>
@@ -270,14 +286,12 @@ const NoteFlow = () => {
setLastNodeId(noNode.id);
} else {
console.log(option);
// Add new node logic
if (addNoteNode) {
const initialX = addNoteNode.position.x;
setNodes((nds) =>
nds.map((node) =>
node.id === '2'
node.key === 'add-node'
? {
...node,
position: {
@@ -303,6 +317,10 @@ const NoteFlow = () => {
id: nanoid(8),
source: lastNodeId,
target: newNodeId
// data: {
// label: <div onClick={handleAddNoteClick}>+</div>,
// onClick: handleAddNoteClick
// }
};
setNodes((nds) => [...nds, newNode]);
@@ -315,15 +333,17 @@ const NoteFlow = () => {
// setEdges((eds) => [
// ...eds,
// {
// id: uuidv4(),
// id: nanoid(8),
// source: lastNodeId,
// data: {
// label: <div onClick={handleAddNoteClick}>Add Note</div>, // Use HTML or React component
// onClick: handleAddNoteClick
// },
// target: newNodeId
// }
// },
// { id: nanoid(8), source: newNodeId, target: addNoteNode.id }
// ]);
// Cuộn tới node mới thêm
setLastNodeId(newNodeId);
}
@@ -340,21 +360,361 @@ const NoteFlow = () => {
setSelectedNodeContent(content); // Cập nhật nội dung được chọn
};
const onDrop = (event) => {
event.preventDefault();
const type = JSON.parse(
event.dataTransfer.getData('application/reactflow')
);
const reactFlowBounds = event.target.getBoundingClientRect();
const position = {
x: event.clientX - reactFlowBounds.left - 100,
y: event.clientY - reactFlowBounds.top + 5
};
const newNodeId = nanoid(8);
// Xử lý add-node với id '2'
const addNodeId = '2';
const addNode = nodes.find((node) => node.id === addNodeId);
// Tìm các edge nối với node có id '2'
const targetEdge = edges.find(
(edge) => edge?.source === '2' || edge?.target === '2'
);
const targetNodeId =
targetEdge?.source === '2' ? targetEdge?.target : targetEdge?.source;
const targetNode = nodes.find((node) => node.id === targetNodeId);
// Cập nhật vị trí của node mới để cách node đang kết nối 200 đơn vị
const newNodePosition = {
x: targetNode?.position.x,
y: targetNode?.position.y + 110
};
// Tạo node mới
const newNode = {
id: newNodeId,
key: type.key,
type: type.type || 'default',
data: {
label: (
<div className='step'>
<div className='left'>
<img src={type.icon} width='24px' height='24px' alt='' />
</div>
<div className='right'>{type.label}</div>
</div>
)
},
position: newNodePosition
};
// Loại bỏ các edge liên quan đến node '2'
setEdges((eds) =>
eds.filter((edge) => edge.source !== '2' && edge.target !== '2')
);
// Kiểm tra điều kiện node là 'ifelse'
if (type.key === 'ifelse') {
setNodes((nds) => nds.filter((node) => node.id !== addNodeId));
const yesNodeId = nanoid(8);
const noNodeId = nanoid(8);
// Tạo node Yes
const yesNode = {
id: yesNodeId,
key: 'add-node',
brand: 'yes',
data: {
label: (
<div className='add-node'>
<i>+</i> <span>Add a journey point</span>
</div>
)
},
position: {
x: newNode.position.x - 200, // Điều chỉnh vị trí của nhánh Yes
y: newNode.position.y + 150 // Điều chỉnh vị trí của nhánh Yes
}
};
// Tạo node No
const noNode = {
id: noNodeId,
key: 'add-node',
brand: 'no',
data: {
label: (
<div className='add-node'>
<i>+</i> <span>Add a journey point</span>
</div>
)
},
position: {
x: newNode?.position.x + 200, // Điều chỉnh vị trí của nhánh No
y: newNode?.position.y + 150 // Điều chỉnh vị trí của nhánh No
}
};
// Tạo các edge nối từ node ifelse đến Yes và No
const ifelseEdges = [
{
id: nanoid(8),
source: targetNodeId,
target: newNodeId
},
{
id: nanoid(8),
source: newNodeId,
target: yesNodeId,
type: 'custom',
data: {
label: 'Yes'
}
},
{
id: nanoid(8),
source: newNodeId,
target: noNodeId,
type: 'custom',
data: {
label: 'No'
}
}
];
// Thêm node yes, no và edge vào danh sách node và edges
setNodes((nds) => [...nds, newNode, yesNode, noNode]);
setEdges((eds) => [...eds, ...ifelseEdges]);
} else {
// Nếu không phải 'ifelse', kiểm tra các điều kiện của node bình thường
if (targetNode) {
setNodes((nds) => [...nds, newNode]);
setEdges((eds) => [
...eds,
{
id: nanoid(8),
source: targetNodeId,
target: newNodeId
},
{
id: nanoid(8),
source: newNodeId,
target: '2'
}
]);
if (addNode) {
const addNodePosition = {
x: addNode.position.x,
y: addNode.position.y + 110
};
setNodes((nds) =>
nds.map((node) =>
node.id === addNodeId
? { ...node, position: addNodePosition }
: node
)
);
setEdges((eds) =>
eds.filter(
(edge) => edge.source !== addNodeId && edge.target !== addNodeId
)
);
// Thêm edge mới nối add-node và node mới
setEdges((eds) => [
...eds,
{
id: nanoid(8),
source: newNodeId,
target: addNodeId
}
]);
// Kiểm tra nếu node mới là 'contact-exists', ẩn add-node
if (type.key === 'contact-exists') {
setNodes((nds) => nds.filter((node) => node.id !== addNodeId));
}
}
} else {
const newNodeNew = {
id: newNodeId,
key: type.key,
type: type.type || 'default',
data: {
label: (
<div className='step'>
<div className='left'>
<img src={type.icon} width='24px' height='24px' alt='' />
</div>
<div className='right'>{type.label}</div>
</div>
)
},
position: position
};
setNodes((nds) => [...nds, newNodeNew]);
}
}
};
const onDragOver = useCallback((event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[setNodes]
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[setEdges]
);
const defaultEdgeOptions = {
style: { strokeWidth: 1.5, stroke: 'black' },
type: 'floating',
markerEnd: {
type: MarkerType.ArrowClosed,
color: 'black'
}
};
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
);
// Hàm tính khoảng cách giữa hai node
const getDistance = (pos1, pos2) => {
return Math.sqrt(
Math.pow(pos1.x - pos2.x, 2) + Math.pow(pos1.y - pos2.y, 2)
);
};
// Hàm xử lý khi dừng kéo node
const onNodeDragStop = useCallback(
(event, node) => {
const threshold = 50; // khoảng cách cho phép để node bị "đè"
let closestNode = null;
let closestDistance = Infinity;
nodes.forEach((item) => {
if (item.id !== node.id) {
const distance = getDistance(node.position, item.position);
if (distance < closestDistance && distance <= threshold) {
closestNode = item;
closestDistance = distance;
}
}
});
if (closestNode) {
// Thay thế node cũ bằng node mới
const updatedEdges = edges.map((edge) => {
if (edge.source === closestNode.id) {
return { ...edge, source: node.id }; // Nối node mới làm source
}
if (edge.target === closestNode.id) {
return { ...edge, target: node.id }; // Nối node mới làm target
}
return edge;
});
if (node.type != 'output') {
// Tạo một node mới với nội dung "Add a journey point" ngay dưới node thay thế
const newNode = {
id: nanoid(8),
key: 'add-node',
brand: node.brand,
position: {
x: node.position.x, // Giữ nguyên vị trí x
y: node.position.y + 100 // Đặt node mới xuống dưới 150 đơn vị y
},
data: {
label: (
<div className='add-node'>
<i>+</i> <span>Add a journey point</span>
</div>
)
}
};
// Thay thế node cũ bằng node mới
setNodes((nds) => [
...nds.map((n) =>
n.id === closestNode.id
? { ...closestNode, ...node, position: node.position }
: n
),
newNode // Thêm node mới
]);
// Cập nhật edges
setEdges([
...updatedEdges,
{
id: nanoid(8), // ID của edge mới
source: node.id, // Nối từ node vừa thay thế
target: newNode.id // Đến node "Add a journey point"
}
]);
} else {
// Thay thế node cũ bằng node mới
setNodes((nds) => [
...nds.map((n) =>
n.id === closestNode.id
? { ...closestNode, ...node, position: node.position }
: n
)
]);
// Cập nhật edges
setEdges(updatedEdges);
}
} else {
// Không có node gần, cập nhật node vào vị trí mới
setNodes((nds) =>
nds.map((n) =>
n.id === node.id ? { ...n, position: node.position } : n
)
);
}
},
[nodes, edges, setNodes, setEdges]
);
return (
<div style={{ height: '100vh', width: '100%', position: 'relative' }}>
<ReactFlow
nodes={nodes}
edges={edges}
edgeTypes={{ custom: CustomEdge }}
onNodeClick={onNodeClick}
onNodeContextMenu={onNodeContextMenu}
panOnScroll
selectionOnDrag
selectionMode={SelectionMode.Partial}
>
<Controls />
<Background variant={BackgroundVariant.Lines} color='#ccccc' />
</ReactFlow>
<div style={{ overflowY: 'auto', position: 'relative' }}>
<div className='flex-flow'>
<MenuFlow />
<div className='panelContainer'>
<ReactFlow
nodes={nodes}
edges={edges}
edgeTypes={{ custom: CustomEdge }}
onNodeClick={onNodeClick}
onNodeContextMenu={onNodeContextMenu}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onDrop={onDrop}
onDragOver={onDragOver}
panOnScroll
defaultEdgeOptions={defaultEdgeOptions}
onConnect={onConnect}
onNodeDragStop={onNodeDragStop}
>
<Controls position='right-bottom' />
<Background variant={BackgroundVariant.Lines} color='#ccccc' />
</ReactFlow>
</div>
</div>
{isTabbarVisible && (
<Tabbar addNode={addNode} closeTabbar={closeTabbar} />