diff --git a/package-lock.json b/package-lock.json index eb9a8f1..cb93192 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,9 +17,11 @@ "@tailwindcss/vite": "^4.1.14", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "framer-motion": "^12.23.24", "lucide-react": "^0.545.0", "react": "^19.1.1", "react-dom": "^19.1.1", + "react-hot-toast": "^2.6.0", "react-redux": "^9.2.0", "react-router-dom": "^7.9.4", "socket.io-client": "^4.8.1", @@ -2572,7 +2574,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, "license": "MIT" }, "node_modules/debug": { @@ -3130,6 +3131,33 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "12.23.24", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz", + "integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.23", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3189,6 +3217,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/goober": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.18.tgz", + "integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3667,6 +3704,21 @@ "node": ">= 18" } }, + "node_modules/motion-dom": { + "version": "12.23.23", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", + "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3847,6 +3899,23 @@ "react": "^19.2.0" } }, + "node_modules/react-hot-toast": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz", + "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", diff --git a/package.json b/package.json index d851ee4..8e82796 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,11 @@ "@tailwindcss/vite": "^4.1.14", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "framer-motion": "^12.23.24", "lucide-react": "^0.545.0", "react": "^19.1.1", "react-dom": "^19.1.1", + "react-hot-toast": "^2.6.0", "react-redux": "^9.2.0", "react-router-dom": "^7.9.4", "socket.io-client": "^4.8.1", @@ -43,4 +45,4 @@ "tw-animate-css": "^1.4.0", "vite": "^7.1.7" } -} \ No newline at end of file +} diff --git a/src/App.jsx b/src/App.jsx index 3c000cf..44174b0 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,8 +1,17 @@ import React from "react"; -import Homepage from "../src/pages/Homepage.jsx"; +import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import Homepage from "./pages/Homepage"; +import SortingPage from "./pages/sorting/SortingPage"; function App() { - return ; + return ( + + + } /> + } /> + + + ); } export default App; diff --git a/src/algorithms/sorting/selectionSort.js b/src/algorithms/sorting/selectionSort.js new file mode 100644 index 0000000..86c2256 --- /dev/null +++ b/src/algorithms/sorting/selectionSort.js @@ -0,0 +1,26 @@ +// src/algorithms/sorting/selectionSort.js +export function* selectionSort(array) { + const arr = [...array]; + const n = arr.length; + + for (let i = 0; i < n - 1; i++) { + let minIndex = i; + yield { type: "compare", indices: [i] }; + + for (let j = i + 1; j < n; j++) { + yield { type: "compare", indices: [minIndex, j] }; + + if (arr[j] < arr[minIndex]) { + minIndex = j; + yield { type: "min", index: minIndex }; + } + } + + if (minIndex !== i) { + [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; + yield { type: "swap", indices: [i, minIndex], array: [...arr] }; + } + } + + yield { type: "done", array: arr }; +} diff --git a/src/components/sorting/SelectionSortVisualizer.jsx b/src/components/sorting/SelectionSortVisualizer.jsx new file mode 100644 index 0000000..e788285 --- /dev/null +++ b/src/components/sorting/SelectionSortVisualizer.jsx @@ -0,0 +1,38 @@ +import React from "react"; + +const COLORS = { + default: "bg-blue-500 shadow-[0_0_10px_#3b82f6]", + comparing: "bg-yellow-400 shadow-[0_0_12px_#facc15]", + min: "bg-green-500 shadow-[0_0_12px_#22c55e]", + swap: "bg-red-500 shadow-[0_0_12px_#ef4444]", +}; + +export default function SelectionSortVisualizer({ array, highlight }) { + const maxValue = Math.max(...array, 1); // Avoid division by zero + const containerHeight = 288; // px (matches h-72) + + return ( +
+ {array.map((value, idx) => { + let color = COLORS.default; + if (highlight?.type === "compare" && highlight.indices?.includes(idx)) + color = COLORS.comparing; + if (highlight?.type === "min" && highlight.index === idx) + color = COLORS.min; + if (highlight?.type === "swap" && highlight.indices?.includes(idx)) + color = COLORS.swap; + + // Normalize height relative to the maximum value + const height = Math.max((value / maxValue) * containerHeight, 15); // minimum 15px for visibility + + return ( +
+ ); + })} +
+ ); +} diff --git a/src/pages/Homepage.jsx b/src/pages/Homepage.jsx index 8d82285..3146225 100644 --- a/src/pages/Homepage.jsx +++ b/src/pages/Homepage.jsx @@ -1,6 +1,8 @@ import React from "react"; import { ArrowRight, Github } from "lucide-react"; import sort from "../assets/sorting.png" +import { useNavigate } from "react-router-dom"; + const sections = [ { @@ -8,7 +10,9 @@ const sections = [ description: "Visualize step-by-step how sorting algorithms organize data efficiently.", phase: "Phase 1 (MVP)", - img: {sort}, + img: "https://tamimehsan.github.io/AlgorithmVisualizer/images/sort.png?height=200&width=300", + link: "/sorting", + flag: false }, { title: "Searching Algorithms", @@ -16,6 +20,8 @@ const sections = [ "Understand linear and binary search methods through live visualization.", phase: "Phase 1 (MVP)", img: "", + link: "/searching", + flag: true }, { title: "Pathfinding Algorithms", @@ -23,6 +29,8 @@ const sections = [ "Watch how A*, Dijkstra and BFS explore grids to find the optimal path.", phase: "Phase 1 (MVP)", img: "", + link: "/pathfinding", + flag: true }, { title: "Graph Algorithms", @@ -30,6 +38,8 @@ const sections = [ "Explore BFS, DFS, Kruskal’s, Prim’s, and more — all brought to life interactively.", phase: "Phase 2", img: "", + link: "/graphs", + flag: true }, { title: "Recursion & Backtracking", @@ -37,6 +47,8 @@ const sections = [ "Visualize recursive calls and backtracking patterns like N-Queens or Sudoku.", phase: "Phase 2", img: "", + link: "/recursion", + flag: true }, { title: "Data Structures Visualization", @@ -44,6 +56,8 @@ const sections = [ "Interactively understand stacks, queues, linked lists, trees, and heaps.", phase: "Phase 2", img: "", + link: "/data-structures", + flag: true }, { title: "Dynamic Programming", @@ -51,11 +65,15 @@ const sections = [ "Step through state transitions and table updates to grasp DP intuitively.", phase: "Phase 3", img: "", + link: "/dynamic-programming", + flag: true }, ]; const Homepage = () => { + const navigate = useNavigate(); + return (
{/* Full-Page Animated Gradient Background */} @@ -93,6 +111,7 @@ const Homepage = () => { {sections.map((section, index) => (
section.link && navigate(section.link)} className="relative group bg-white/10 border border-white/10 rounded-2xl overflow-hidden shadow-2xl hover:shadow-indigo-500/40 transition-all duration-300 backdrop-blur-md hover:-translate-y-2" > {/* Image */} @@ -119,9 +138,11 @@ const Homepage = () => {
{/* SaaS-style “Coming Soon” Overlay */} -
- Coming Soon 🚀 -
+ {section.flag && ( +
+ Coming Soon 🚀 +
+)}
))} diff --git a/src/pages/sorting/SelectionSort.jsx b/src/pages/sorting/SelectionSort.jsx new file mode 100644 index 0000000..13a51d3 --- /dev/null +++ b/src/pages/sorting/SelectionSort.jsx @@ -0,0 +1,83 @@ +import React, { useState } from "react"; +import { Toaster } from "react-hot-toast"; +import SelectionSortVisualizer from "../../components/sorting/SelectionSortVisualizer"; +import { selectionSort } from "../../algorithms/sorting/selectionSort"; + +export default function SelectionSort() { + const [array, setArray] = useState([]); + const [input, setInput] = useState(""); + const [highlight, setHighlight] = useState(null); + const [isRunning, setIsRunning] = useState(false); + + const handleStart = async () => { + if (isRunning || array.length === 0) return; + setIsRunning(true); + + const gen = selectionSort(array); + for (let step of gen) { + setHighlight(step); + if (step.array) setArray([...step.array]); + await new Promise((r) => setTimeout(r, 500)); + } + + setHighlight({ type: "done" }); + setIsRunning(false); + }; + + const handleReset = () => { + setArray([]); + setInput(""); + setHighlight(null); + }; + + const handleInput = (e) => { + setInput(e.target.value); + const numbers = e.target.value + .split(",") + .map((n) => parseInt(n.trim())) + .filter((n) => !isNaN(n)); + setArray(numbers); + }; + + return ( +
+ +

+ Selection Sort Visualizer +

+ + + +
+ + +
+ +
+ +
+ +
+ ); +} diff --git a/src/pages/sorting/SortingPage.jsx b/src/pages/sorting/SortingPage.jsx new file mode 100644 index 0000000..e91d8c4 --- /dev/null +++ b/src/pages/sorting/SortingPage.jsx @@ -0,0 +1,63 @@ +// src/pages/SortingPage.jsx +import React, { useState } from "react"; +import SelectionSort from "./SelectionSort"; + +export default function SortingPage() { + const [selectedAlgo, setSelectedAlgo] = useState(""); + + const renderAlgorithm = () => { + switch (selectedAlgo) { + case "selection": + return ; + // You can add more later like: + // case "bubble": return ; + // case "merge": return ; + default: + return ( +
+ Select an algorithm to visualize 👆 +
+ ); + } + }; + + return ( +
+ {/* Left Sidebar */} +
+

+ Sorting Panel +

+ + + + + + + + ← Back to Home + +
+ + {/* Right Visualization Area */} +
+ {renderAlgorithm()} +
+
+ ); +}