Skip to content

Commit 9939a6e

Browse files
Merge pull request #38 from mukeshdhadhariya/main
feat(stack): Add Stack Visualization and Home Page for Data Structure Visualizer
2 parents 1c04e80 + f6d0a96 commit 9939a6e

File tree

5 files changed

+347
-0
lines changed

5 files changed

+347
-0
lines changed

src/App.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
44
import SortingPage from "./pages/sorting/SortingPage";
55
import GraphPage from "./pages/graph/GraphPage";
66
import Homepage from "./pages/Homepage.jsx";
7+
import DSPage from "./pages/dataStructure/datastructurePage.jsx"
78
import DynamicProgrammingPage from "./pages/dynamic-programming/DyanmicProgrammingPage.jsx";
89

910
function App() {
@@ -13,6 +14,7 @@ function App() {
1314
<Route path="/" element={<Homepage />} />
1415
{/* <Route path="/graph/union-find" element={<UnionFindPage />} /> */}
1516
<Route path="/sorting" element={<SortingPage />} />
17+
<Route path="/data-structures" element={<DSPage/>}/>
1618
<Route path="/graph" element={<GraphPage />} />
1719
<Route path="/dynamic-programming" element={<DynamicProgrammingPage />} />
1820
</Routes>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
export function* stackOp(array, action) {
2+
3+
const arr = [...array];
4+
5+
const snapshot = () => [...arr];
6+
7+
switch (action?.type) {
8+
case "push": {
9+
yield { type: "push-start", value: action.value };
10+
arr.unshift(action.value);
11+
yield { type: "push", value: action.value, array: snapshot() };
12+
break;
13+
}
14+
15+
case "pop": {
16+
if (arr.length === 0) {
17+
yield { type: "underflow" };
18+
} else {
19+
yield { type: "pop-start", index: 0, value: arr[0] };
20+
const popped = arr.shift();
21+
yield { type: "pop", value: popped, array: snapshot() };
22+
}
23+
break;
24+
}
25+
26+
case "peek": {
27+
if (arr.length === 0) {
28+
yield { type: "peek-empty" };
29+
} else {
30+
yield { type: "peek", value: arr[0], index: 0 };
31+
}
32+
break;
33+
}
34+
35+
case "clear": {
36+
if (arr.length === 0) {
37+
yield { type: "clear-empty" };
38+
} else {
39+
yield { type: "clear", array: [] };
40+
arr.length = 0;
41+
}
42+
break;
43+
}
44+
45+
default: {
46+
yield { type: "invalid", action };
47+
}
48+
}
49+
50+
yield { type: "done", array: snapshot() };
51+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import React from "react";
2+
3+
const COLORS = {
4+
default: "bg-slate-700 shadow-[0_0_8px_rgba(15,23,42,0.6)]",
5+
push: "bg-green-500 shadow-[0_0_12px_rgba(34,197,94,0.4)]",
6+
pop: "bg-red-500 shadow-[0_0_12px_rgba(239,68,68,0.35)]",
7+
peek: "bg-yellow-400 shadow-[0_0_12px_rgba(250,204,21,0.3)]",
8+
underflow: "bg-rose-600/60",
9+
};
10+
11+
export default function StackVisualizer({ array = [], highlight = null }) {
12+
return (
13+
<div className="flex flex-col items-center">
14+
<div className="mb-2 text-sm text-slate-400">Top</div>
15+
16+
<div className="w-48 min-h-[200px] max-h-[420px] border rounded-lg p-3 bg-white/5 overflow-auto">
17+
<div className="flex flex-col items-center space-y-2">
18+
{array.length === 0 ? (
19+
<div className="text-slate-400 italic py-6">Stack is empty</div>
20+
) : (
21+
array.map((value, idx) => {
22+
let cls = COLORS.default;
23+
24+
if (highlight) {
25+
if (highlight.type === "push" && highlight.array && highlight.array[0] === value && idx === 0) {
26+
cls = COLORS.push;
27+
}
28+
if ((highlight.type === "pop" || highlight.type === "pop-start") && idx === 0) {
29+
cls = COLORS.pop;
30+
}
31+
if (highlight.type === "peek" && highlight.index === idx) {
32+
cls = COLORS.peek;
33+
}
34+
if (highlight.type === "underflow") {
35+
cls = COLORS.underflow;
36+
}
37+
if (highlight.type === "clear" && array.length === 0) {
38+
cls = COLORS.default;
39+
}
40+
}
41+
42+
return (
43+
<div
44+
key={idx + "-" + String(value)}
45+
className={`${cls} w-full rounded-md px-3 py-2 flex justify-between items-center text-white transition-all duration-300`}
46+
style={{ minHeight: 40 }}
47+
>
48+
<div className="truncate">{String(value)}</div>
49+
<div className="text-xs opacity-80">#{array.length - idx}</div>
50+
</div>
51+
);
52+
})
53+
)}
54+
</div>
55+
</div>
56+
57+
<div className="mt-2 text-sm text-slate-500">Bottom</div>
58+
</div>
59+
);
60+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React, { useState } from "react";
2+
import { Network, Compass, Rocket } from "lucide-react";
3+
import StackPage from "./stack.jsx";
4+
5+
export default function DSPage() {
6+
const [selectedDS, setSelectedDS] = useState("");
7+
8+
const renderDataStructure = () => {
9+
switch (selectedDS) {
10+
case "stack":
11+
return (
12+
<div className="w-full h-full overflow-auto">
13+
<StackPage />
14+
</div>
15+
);
16+
17+
default:
18+
return (
19+
<div className="flex flex-col items-center justify-center text-center p-6">
20+
<div className="bg-gradient-to-tr from-indigo-500 via-blue-400 to-purple-500 p-[2px] rounded-2xl">
21+
<div className="bg-gray-950 rounded-2xl px-10 py-12 shadow-2xl">
22+
<div className="flex justify-center mb-4">
23+
<Network className="w-16 h-16 text-indigo-400" />
24+
</div>
25+
<h2 className="text-2xl font-extrabold bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent mb-2">
26+
Data Structure Visualizer
27+
</h2>
28+
<p className="text-gray-400 mb-6 max-w-sm">
29+
Select a data structure from the sidebar to begin visualization.
30+
Watch how elements, stacks, and queues transform step by step! 🧠✨
31+
</p>
32+
<div className="flex items-center justify-center gap-6">
33+
<Compass className="w-8 h-8 text-blue-400 animate-pulse" />
34+
<Rocket className="w-8 h-8 text-purple-400 animate-bounce" />
35+
</div>
36+
</div>
37+
</div>
38+
</div>
39+
);
40+
}
41+
};
42+
43+
return (
44+
<div className="flex h-screen bg-black text-white">
45+
{/* Sidebar */}
46+
<div className="w-64 bg-[#0f172a] p-6 border-r border-gray-800 flex-shrink-0">
47+
<h2 className="text-xl font-bold mb-6 text-indigo-400 tracking-wide">
48+
Data Structure Panel
49+
</h2>
50+
51+
<label className="block mb-2 text-sm">Select Data Structure:</label>
52+
<select
53+
value={selectedDS}
54+
onChange={(e) => setSelectedDS(e.target.value)}
55+
className="w-full p-2 rounded bg-[#1e293b] text-white border border-gray-600 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
56+
>
57+
<option value="">Select Data Structure</option>
58+
<option value="stack">Stack</option>
59+
<option value="queue">Queue</option>
60+
{/* <option value="linkedlist">Linked List</option> */}
61+
</select>
62+
63+
<button
64+
onClick={() => setSelectedDS("")}
65+
className="w-full mt-4 py-2 bg-gray-700 hover:bg-gray-600 rounded transition font-medium"
66+
>
67+
Reset
68+
</button>
69+
70+
<a
71+
href="/"
72+
className="inline-block mt-10 text-indigo-400 hover:underline text-sm"
73+
>
74+
← Back to Home
75+
</a>
76+
</div>
77+
78+
{/* Visualization Area */}
79+
<div className="flex-1 flex overflow-auto">
80+
<div className="flex-1 min-w-[800px] min-h-full">
81+
{renderDataStructure()}
82+
</div>
83+
</div>
84+
</div>
85+
);
86+
}

src/pages/dataStructure/stack.jsx

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import React, { useState } from "react";
2+
import { Toaster, toast } from "react-hot-toast";
3+
import StackVisualizer from "../../components/dataStructure/stack.jsx";
4+
import { stackOp } from "../../algorithms/dataStructure/stack.js";
5+
6+
export default function StackPage() {
7+
const [array, setArray] = useState([]);
8+
const [input, setInput] = useState("");
9+
const [highlight, setHighlight] = useState(null);
10+
const [isRunning, setIsRunning] = useState(false);
11+
12+
const delay = (ms) => new Promise((r) => setTimeout(r, ms));
13+
14+
const runAction = async (action) => {
15+
if (isRunning) return;
16+
setIsRunning(true);
17+
18+
const gen = stackOp(array, action);
19+
for (let step of gen) {
20+
setHighlight(step);
21+
if (step.array) setArray([...step.array]);
22+
await delay(400);
23+
}
24+
25+
await delay(150);
26+
setHighlight(null);
27+
setIsRunning(false);
28+
};
29+
30+
const handlePush = async () => {
31+
if (!input.trim()) {
32+
toast.error("Enter a value to push");
33+
return;
34+
}
35+
const parsed = input.trim();
36+
const value = parsed.length && !Number.isNaN(Number(parsed)) ? Number(parsed) : parsed;
37+
await runAction({ type: "push", value });
38+
setInput("");
39+
};
40+
41+
const handlePop = async () => {
42+
await runAction({ type: "pop" });
43+
};
44+
45+
const handlePeek = async () => {
46+
await runAction({ type: "peek" });
47+
};
48+
49+
const handleClear = async () => {
50+
await runAction({ type: "clear" });
51+
};
52+
53+
const handleReset = () => {
54+
setArray([]);
55+
setInput("");
56+
setHighlight(null);
57+
};
58+
59+
const sampleLoad = () => {
60+
setArray(["X", "Y", "Z"]);
61+
setHighlight({ type: "done", array: ["X", "Y", "Z"] });
62+
};
63+
64+
return (
65+
<div className="min-h-screen bg-black text-gray-200 flex flex-col items-center p-4 sm:p-6">
66+
<Toaster position="top-center" />
67+
<h1 className="text-3xl sm:text-4xl font-extrabold mb-6 sm:mb-8 text-indigo-400 drop-shadow-lg text-center">
68+
Stack Visualizer
69+
</h1>
70+
71+
<div className="w-full max-w-2xl">
72+
<div className="bg-gray-900/40 rounded-lg p-4 sm:p-6 shadow-md">
73+
<div className="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-4">
74+
<input
75+
value={input}
76+
onChange={(e) => setInput(e.target.value)}
77+
placeholder="Value to push (number or string)"
78+
className="flex-1 w-full sm:w-auto border-2 border-indigo-500 bg-gray-900 text-indigo-200 rounded-lg p-3 text-center outline-none shadow-inner"
79+
/>
80+
<button
81+
onClick={handlePush}
82+
disabled={isRunning}
83+
className={`w-full sm:w-auto ${
84+
isRunning ? "bg-indigo-700 cursor-not-allowed" : "bg-indigo-600 hover:bg-indigo-500"
85+
} px-4 py-2 rounded text-white font-semibold`}
86+
>
87+
Push
88+
</button>
89+
</div>
90+
91+
<div className="mt-4 grid grid-cols-1 sm:grid-cols-5 gap-3">
92+
<button
93+
onClick={handlePop}
94+
disabled={isRunning}
95+
className={`col-span-1 w-full ${
96+
isRunning ? "bg-gray-700 cursor-not-allowed" : "bg-rose-600 hover:bg-rose-500"
97+
} px-4 py-2 rounded text-white font-semibold`}
98+
>
99+
Pop
100+
</button>
101+
102+
<button
103+
onClick={handlePeek}
104+
disabled={isRunning}
105+
className={`col-span-1 w-full ${
106+
isRunning ? "bg-gray-700 cursor-not-allowed" : "bg-yellow-500 hover:bg-yellow-400"
107+
} px-4 py-2 rounded text-white font-semibold`}
108+
>
109+
Peek
110+
</button>
111+
112+
<button
113+
onClick={handleClear}
114+
disabled={isRunning}
115+
className={`col-span-1 w-full ${
116+
isRunning ? "bg-gray-700 cursor-not-allowed" : "bg-gray-700 hover:bg-gray-600"
117+
} px-4 py-2 rounded text-white font-semibold`}
118+
>
119+
Clear
120+
</button>
121+
122+
<button
123+
onClick={handleReset}
124+
className="col-span-1 w-full bg-gray-600 hover:bg-gray-500 px-4 py-2 rounded text-white font-semibold"
125+
>
126+
Reset
127+
</button>
128+
129+
<button
130+
onClick={sampleLoad}
131+
className="col-span-1 w-full bg-indigo-500 hover:bg-indigo-400 px-4 py-2 rounded text-white font-semibold"
132+
>
133+
Load Demo
134+
</button>
135+
</div>
136+
</div>
137+
138+
<div className="mt-6">
139+
<StackVisualizer array={array} highlight={highlight} />
140+
</div>
141+
142+
<div className="mt-4 text-sm text-slate-400 text-center">
143+
Tip: top of stack is shown at the top (index 0). Push adds to top; Pop removes the top (LIFO).
144+
</div>
145+
</div>
146+
</div>
147+
);
148+
}

0 commit comments

Comments
 (0)