Skip to content

Commit 15a2b01

Browse files
committed
[ADD] the current version of the reactjs tree to the react folder
1 parent 097a729 commit 15a2b01

File tree

1 file changed

+315
-0
lines changed
  • react/src/app/tree_recreation_dynamic

1 file changed

+315
-0
lines changed
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
/*
2+
** EPITECH PROJECT, 2024
3+
** epickup
4+
** File description:
5+
** page.js
6+
*/
7+
8+
"use client";
9+
import React, { useEffect, useRef, useState } from 'react';
10+
import * as d3 from 'd3';
11+
12+
const DynamicTreeGraph = () => {
13+
const svgRef = useRef(null);
14+
15+
// Initial game data
16+
const initialData = {
17+
name: "Start Game",
18+
id: "start",
19+
completed: true,
20+
current: true,
21+
children: [
22+
{
23+
name: "Choose Character",
24+
id: "character",
25+
completed: false,
26+
current: false,
27+
children: [
28+
{
29+
name: "Warrior",
30+
id: "warrior",
31+
completed: false,
32+
current: false,
33+
},
34+
{
35+
name: "Mage",
36+
id: "mage",
37+
completed: false,
38+
current: false,
39+
}
40+
]
41+
},
42+
{
43+
name: "Tutorial",
44+
id: "tutorial",
45+
completed: false,
46+
current: false,
47+
children: [
48+
{
49+
name: "Basic Controls",
50+
id: "controls",
51+
completed: false,
52+
current: false,
53+
}
54+
]
55+
}
56+
]
57+
};
58+
59+
// State to manage the game data
60+
const [gameData, setGameData] = useState(initialData);
61+
62+
// Function to update node data
63+
const updateNode = (nodeId, updates) => {
64+
const updateNodeData = (node) => {
65+
if (node.id === nodeId) {
66+
return { ...node, ...updates };
67+
}
68+
69+
if (node.children) {
70+
return {
71+
...node,
72+
children: node.children.map(child => updateNodeData(child))
73+
};
74+
}
75+
76+
return node;
77+
};
78+
79+
setGameData(prevData => updateNodeData(prevData));
80+
};
81+
82+
// Function to handle node clicks
83+
const handleNodeClick = (nodeId) => {
84+
// Find the current node
85+
const findNode = (node) => {
86+
if (node.id === nodeId) return node;
87+
if (node.children) {
88+
for (const child of node.children) {
89+
const found = findNode(child);
90+
if (found) return found;
91+
}
92+
}
93+
return null;
94+
};
95+
96+
const clickedNode = findNode(gameData);
97+
98+
// Reset current flag for all nodes
99+
const resetCurrentNodes = (node) => {
100+
if (node.current) {
101+
updateNode(node.id, { current: false });
102+
}
103+
104+
if (node.children) {
105+
node.children.forEach(resetCurrentNodes);
106+
}
107+
};
108+
109+
resetCurrentNodes(gameData);
110+
111+
// Set the clicked node as current and completed
112+
updateNode(nodeId, { current: true, completed: true });
113+
114+
console.log(`Node clicked: ${nodeId}`);
115+
};
116+
117+
// Function to handle button clicks
118+
const handleButtonClick = (nodeId, action) => {
119+
console.log(`Button clicked on node ${nodeId}: ${action}`);
120+
// Implement your button actions here
121+
};
122+
123+
// Render the tree whenever the data changes
124+
useEffect(() => {
125+
if (!svgRef.current) return;
126+
127+
// Clear previous rendering
128+
d3.select(svgRef.current).selectAll("*").remove();
129+
130+
// Create a deep copy of the data with click handlers
131+
const processedData = JSON.parse(JSON.stringify(gameData));
132+
133+
// Add click handlers to the processed data
134+
const addHandlers = (node) => {
135+
node.onClick = () => handleNodeClick(node.id);
136+
137+
// Add buttons if needed
138+
if (node.completed) {
139+
node.buttons = [
140+
{
141+
label: "Info",
142+
onClick: () => handleButtonClick(node.id, "info")
143+
},
144+
{
145+
label: "Reset",
146+
onClick: () => handleButtonClick(node.id, "reset")
147+
}
148+
];
149+
}
150+
151+
if (node.children) {
152+
node.children.forEach(addHandlers);
153+
}
154+
};
155+
156+
addHandlers(processedData);
157+
158+
// Define dimensions and margins
159+
const margin = { top: 20, right: 90, bottom: 30, left: 90 };
160+
const width = 800 - margin.left - margin.right;
161+
const height = 600 - margin.top - margin.bottom;
162+
163+
// Create SVG element
164+
const svg = d3.select(svgRef.current)
165+
.attr("width", width + margin.left + margin.right)
166+
.attr("height", height + margin.top + margin.bottom)
167+
.append("g")
168+
.attr("transform", `translate(${margin.left},${margin.top})`);
169+
170+
// Create the tree layout
171+
const treeLayout = d3.tree().size([height, width]);
172+
173+
// Create the root node
174+
const root = d3.hierarchy(processedData);
175+
176+
// Assign positions to nodes
177+
treeLayout(root);
178+
179+
// Create links
180+
const link = svg.selectAll(".link")
181+
.data(root.links())
182+
.enter()
183+
.append("path")
184+
.attr("class", "link")
185+
.attr("d", d3.linkHorizontal()
186+
.x(d => d.y)
187+
.y(d => d.x))
188+
.attr("fill", "none")
189+
.attr("stroke", d => d.target.data.completed ? "#48bb78" : "#cbd5e0")
190+
.attr("stroke-width", d => d.target.data.completed ? 2 : 1);
191+
192+
// Create node groups
193+
const node = svg.selectAll(".node")
194+
.data(root.descendants())
195+
.enter()
196+
.append("g")
197+
.attr("class", "node")
198+
.attr("transform", d => `translate(${d.y},${d.x})`)
199+
.attr("cursor", "pointer")
200+
.on("click", (event, d) => {
201+
if (typeof d.data.onClick === 'function') {
202+
d.data.onClick();
203+
}
204+
});
205+
206+
// Add rectangles for nodes
207+
node.append("rect")
208+
.attr("width", 120)
209+
.attr("height", 40)
210+
.attr("rx", 8)
211+
.attr("ry", 8)
212+
.attr("x", -60)
213+
.attr("y", -20)
214+
.attr("fill", d => {
215+
if (d.data.current) return "#4299e1"; // Current node
216+
if (d.data.completed) return "#48bb78"; // Completed node
217+
return "#a0aec0"; // Regular node
218+
})
219+
.attr("stroke", d => {
220+
if (d.data.current) return "#2b6cb0"; // Current node
221+
if (d.data.completed) return "#2f855a"; // Completed node
222+
return "#718096"; // Regular node
223+
})
224+
.attr("stroke-width", d => d.data.current ? 2 : 1);
225+
226+
// Add text labels
227+
node.append("text")
228+
.attr("dy", "0.35em")
229+
.attr("text-anchor", "middle")
230+
.attr("fill", "white")
231+
.attr("font-weight", "bold")
232+
.attr("pointer-events", "none")
233+
.text(d => d.data.name);
234+
235+
// Add buttons/links if specified
236+
node.each(function (d) {
237+
if (d.data.buttons) {
238+
const buttonGroup = d3.select(this).append("g")
239+
.attr("transform", "translate(0, 25)");
240+
241+
d.data.buttons.forEach((button, index) => {
242+
const buttonWidth = 50;
243+
const spacing = 5;
244+
const totalWidth = (buttonWidth + spacing) * d.data.buttons.length - spacing;
245+
const startX = -totalWidth / 2;
246+
247+
buttonGroup.append("rect")
248+
.attr("width", buttonWidth)
249+
.attr("height", 20)
250+
.attr("rx", 4)
251+
.attr("ry", 4)
252+
.attr("x", startX + index * (buttonWidth + spacing))
253+
.attr("y", 0)
254+
.attr("fill", "#4299e1")
255+
.attr("cursor", "pointer")
256+
.on("click", (event) => {
257+
event.stopPropagation();
258+
if (typeof button.onClick === 'function') {
259+
button.onClick();
260+
}
261+
});
262+
263+
buttonGroup.append("text")
264+
.attr("x", startX + index * (buttonWidth + spacing) + buttonWidth / 2)
265+
.attr("y", 10)
266+
.attr("dy", "0.35em")
267+
.attr("text-anchor", "middle")
268+
.attr("fill", "white")
269+
.attr("font-size", "10px")
270+
.attr("pointer-events", "none")
271+
.text(button.label);
272+
});
273+
}
274+
});
275+
276+
}, [gameData]);
277+
278+
// Reset the tree
279+
const resetTree = () => {
280+
setGameData(initialData);
281+
};
282+
283+
return (
284+
<div className="p-4">
285+
<h1 className="text-2xl font-bold mb-4">Game Progress Tree</h1>
286+
<div className="mb-4 flex space-x-4">
287+
<div className="flex items-center">
288+
<div className="w-4 h-4 bg-green-500 mr-2"></div>
289+
<span>Completed</span>
290+
</div>
291+
<div className="flex items-center">
292+
<div className="w-4 h-4 bg-blue-500 mr-2"></div>
293+
<span>Current Position</span>
294+
</div>
295+
<div className="flex items-center">
296+
<div className="w-4 h-4 bg-gray-400 mr-2"></div>
297+
<span>Not Visited</span>
298+
</div>
299+
</div>
300+
<div className="mb-4">
301+
<button
302+
onClick={resetTree}
303+
className="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded"
304+
>
305+
Reset Progress
306+
</button>
307+
</div>
308+
<div className="border border-gray-300 rounded-lg overflow-hidden">
309+
<svg ref={svgRef}></svg>
310+
</div>
311+
</div>
312+
);
313+
};
314+
315+
export default DynamicTreeGraph;

0 commit comments

Comments
 (0)