This commit is contained in:
2024-09-25 17:20:34 +07:00
parent 3762dbe776
commit f89d9e3658
3 changed files with 249 additions and 25 deletions

View File

@@ -759,3 +759,63 @@ foreignObject {
font-weight: 600;
line-height: 1.6;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5); /* Màu đen mờ */
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
/* Nội dung modal */
.modal-content {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
text-align: center;
width: 400px;
}
/* Các nút trong modal */
.modal-content button {
margin: 10px;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
/* Style cho nút Yes, No, Both */
.modal-content button:nth-child(1) {
background-color: #28a745; /* Màu xanh cho Yes */
color: white;
}
.modal-content button:nth-child(2) {
background-color: #dc3545; /* Màu đỏ cho No */
color: white;
}
.modal-content button:nth-child(3) {
background-color: #ffc107; /* Màu vàng cho Both */
color: black;
}
/* Nút Cancel */
.modal-content button:nth-child(4) {
background-color: #6c757d; /* Màu xám cho Cancel */
color: white;
}
/* Hover effect cho các nút */
.modal-content button:hover {
opacity: 0.8;
}

View File

@@ -10,6 +10,7 @@ import {
ReactFlowProvider,
useNodesState,
useEdgesState,
useStoreApi,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import '../assets/css/style.css';
@@ -18,6 +19,7 @@ import ShowPopup from './ShowPopup.tsx';
import { nanoid } from 'nanoid';
import CustomEdge from './CustomEdge';
import MenuFlow from './MenuFlow';
import DeleteNodeModal from './popup/DeleteNodeModal.tsx';
// Nội dung cho mỗi node
const nodeContents = {
@@ -53,8 +55,8 @@ const nodeContents = {
};
const NodeFlow = ({ initialNodes, initialEdges }) => {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const [showTabBar, setShowTabBar] = useState(false);
const [lastNodeId, setLastNodeId] = useState(nodes[0].id);
const [showPopup, setShowPopup] = useState(false);
@@ -64,6 +66,9 @@ const NodeFlow = ({ initialNodes, initialEdges }) => {
const { screenToFlowPosition } = useReactFlow();
const [showActions, setShowActions] = useState(null);
const [showPopupSuccess, setShowPopupSuccess] = useState(false);
const store = useStoreApi();
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deleteNodeId, setDeleteNodeId] = useState(null);
const handleAddNoteClick = () => {
setShowTabBar(true);
@@ -337,7 +342,7 @@ const NodeFlow = ({ initialNodes, initialEdges }) => {
Sửa
</button>
<button
onClick={() => handleDeleteNode(newNodeId)}
onClick={() => handleDeleteNode(newNodeId, type.key)}
className="btn-remove"
>
Xóa
@@ -505,8 +510,6 @@ const NodeFlow = ({ initialNodes, initialEdges }) => {
}
}
}
console.log('Updated nodes:', nodes);
};
const onDragOver = useCallback((event) => {
@@ -546,15 +549,33 @@ const NodeFlow = ({ initialNodes, initialEdges }) => {
// Hàm xóa node
const handleDeleteNode = useCallback(
(nodeId) => {
(nodeId, nodeKey) => {
// Tìm tất cả các edges có nodeId là source hoặc target
const connectedEdges = edges.filter(
const allNode = store.getState();
const edgeNode = allNode.edges;
const Node = allNode.nodes;
if (nodeKey === 'ifelse') {
setDeleteNodeId(nodeId); // Lưu lại ID của node muốn xóa
// Hiển thị modal để người dùng chọn nhánh xóa
setIsDeleteModalOpen(true);
document
.querySelector('.actions-' + nodeId)
?.classList.remove('staticNode');
} else {
const isConfirmed = window.confirm(
'Bạn có chắc chắn muốn xóa node này không?'
);
if (!isConfirmed) return;
// Tìm tất cả các edges có nodeId là source hoặc target
const connectedEdges = edgeNode.filter(
(edge) => edge.source === nodeId || edge.target === nodeId
);
console.log(connectedEdges);
console.log('connectedEdges', connectedEdges);
// Lấy các nodes đang nối với node bị xóa
const sourceNode = connectedEdges.find(
(edge) => edge.target === nodeId
)?.source;
@@ -562,16 +583,28 @@ const NodeFlow = ({ initialNodes, initialEdges }) => {
(edge) => edge.source === nodeId
)?.target;
// Cập nhật nodes: xóa node bị xóa
setNodes((nds) => nds.filter((node) => node.id !== nodeId));
// Xóa node và các node con
setNodes((nds) => {
const updatedNodes = nds.filter((node) => node.id !== nodeId);
// Cập nhật edges: xóa các cạnh kết nối với node bị xóa
// Cập nhật vị trí cho các node còn lại
const sortedNodes = updatedNodes.sort(
(a, b) => a.position.y - b.position.y
);
return sortedNodes.map((node, index) => ({
...node,
position: {
...node.position,
y: index * 110, // Khoảng cách giữa các node
},
}));
});
// Xóa các cạnh kết nối với node bị xóa và các node con
setEdges((eds) =>
eds.filter((edge) => edge.source !== nodeId && edge.target !== nodeId)
);
// Nếu có cả sourceNode và targetNode, tạo một edge mới nối 2 node này
if (sourceNode && targetNode) {
setEdges((eds) => [
...eds,
{
@@ -585,6 +618,111 @@ const NodeFlow = ({ initialNodes, initialEdges }) => {
[edges, setNodes, setEdges]
);
// Hàm xác định và xóa nhánh dựa trên node đã chọn
const handleConfirmDeleteBranchNode = (branch) => {
const allNode = store.getState();
const Node = allNode.nodes;
const edgeNode = allNode.edges;
// Tìm node ifelse
const ifelseNode = allNode.nodes.find((node) => node.id === deleteNodeId);
// Tìm edges của nhánh được chọn (Yes hoặc No)
const branchEdges = edgeNode.filter((edge) => edge.data?.label === branch);
// Lấy ID của tất cả các node trong nhánh dựa trên các edges
const branchNodeIds = branchEdges.map((edge) => edge.target);
// Lấy danh sách các node trong nhánh dựa trên cùng tọa độ x với node được chọn
const selectedNode = allNode.nodes.find(
(node) => node.id === branchNodeIds[0]
);
const nodesToDelete = allNode.nodes
.filter((node) => node.position.x === selectedNode.position.x) // Lọc các node có cùng tọa độ x với node được chọn
.map((node) => node.id); // Lấy ID của các node trong nhánh
// Bao gồm cả node ifelse trong danh sách node cần xóa
nodesToDelete.push(ifelseNode.id);
// Tìm edges nối đến nhánh còn lại
const remainingEdges = edgeNode.filter(
(edge) => edge.source === ifelseNode.id && edge.data?.label !== branch
);
const TargetIfelse = edgeNode.filter(
(edge) => edge.target === ifelseNode.id
);
const NodeRemaining = allNode.nodes.find(
(node) => node.id === remainingEdges[0].target
);
// Xóa tất cả các node trong nhánh
setNodes((nds) => {
const updatedNodes = nds.filter(
(node) => !nodesToDelete.includes(node.id)
);
// Nếu còn node sau khi xóa
if (updatedNodes.length > 0) {
// Lấy tất cả các node của nhánh còn lại
const nodesToRemaining = allNode.nodes
.filter((node) => node.position.x === NodeRemaining.position.x) // Lọc các node có cùng tọa độ x với node được chọn
.map((node) => node); // Lấy các node trong nhánh
// Đặt lại vị trí của tất cả các node còn lại theo thứ tự và khoảng cách đều
const updatedRemainingNodes = nodesToRemaining.map((node, index) => ({
...node,
position: {
x: ifelseNode.position.x,
y: ifelseNode.position.y + index * 110,
},
}));
// Kết hợp lại với các node khác (nếu có)
return updatedNodes.map((node) => {
const updatedNode = updatedRemainingNodes.find(
(n) => n.id === node.id
);
return updatedNode ? updatedNode : node;
});
}
return updatedNodes; // Trả về danh sách nodes đã được cập nhật
});
// Xóa các edges liên quan đến nhánh được chọn
setEdges((eds) => {
const updatedEdges = [
...eds.filter(
(edge) =>
!nodesToDelete.includes(edge.source) &&
!nodesToDelete.includes(edge.target)
),
{
id: `edge-${NodeRemaining.id}-${TargetIfelse[0].source}`,
source: NodeRemaining.id,
target: TargetIfelse[0].source,
},
];
return updatedEdges;
});
setIsDeleteModalOpen(false); // Đóng modal
};
// Hàm xử lý xác nhận xóa
const handleConfirmDelete = (choice) => {
if (choice === 'yes') {
handleConfirmDeleteBranchNode('Yes');
} else if (choice === 'no') {
handleConfirmDeleteBranchNode('No');
} else if (choice === 'both') {
handleConfirmDeleteBranchNode('Yes');
handleConfirmDeleteBranchNode('No');
}
};
// Hàm sửa node
const handleEditNode = (nodeId: string) => {
// Logic chỉnh sửa node
@@ -614,6 +752,8 @@ const NodeFlow = ({ initialNodes, initialEdges }) => {
panOnScroll
defaultEdgeOptions={defaultEdgeOptions}
onConnect={onConnect}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
>
<Controls position="right-bottom" />
<Background variant={BackgroundVariant.Lines} color="#ccccc" />
@@ -652,6 +792,13 @@ const NodeFlow = ({ initialNodes, initialEdges }) => {
</div>
</div>
)}
{isDeleteModalOpen && (
<DeleteNodeModal
onConfirm={handleConfirmDelete}
onClose={() => setIsDeleteModalOpen(false)}
/>
)}
</div>
);
};

View File

@@ -0,0 +1,17 @@
import React, { useState, useCallback } from 'react';
const DeleteNodeModal = ({ onConfirm, onClose }) => {
return (
<div className="modal-overlay">
<div className="modal-content">
<h3>Bạn muốn xóa nhánh nào?</h3>
<button onClick={() => onConfirm('yes')}>Yes</button>
<button onClick={() => onConfirm('no')}>No</button>
<button onClick={() => onConfirm('both')}>Both</button>
<button onClick={() => onClose()}>Cancel</button>
</div>
</div>
);
};
export default DeleteNodeModal;