From 1b8dc20efe37aeea4e6068510c6b9fde64515f48 Mon Sep 17 00:00:00 2001 From: TamimEhsan Date: Mon, 25 May 2026 12:23:25 +0800 Subject: [PATCH 01/14] feat: Add running state management to various components and disable buttons accordingly --- src/app/15-puzzle/page.jsx | 6 +- src/app/convex-hull/page.jsx | 3 +- src/app/game-of-life/menu.jsx | 11 ++- src/app/game-of-life/page.jsx | 1 + src/app/pathfinder/menu.jsx | 13 ++-- src/app/pathfinder/page.jsx | 107 +++++++++++------------------ src/app/prime-numbers/menu.jsx | 2 +- src/app/recursion-tree/menu.jsx | 3 + src/app/recursion-tree/page.jsx | 13 ++-- src/app/recursive-sorting/menu.jsx | 2 +- src/app/sorting/menu.jsx | 8 +-- src/app/turing-machine/menu.jsx | 3 + src/app/turing-machine/page.jsx | 7 +- src/components/custom-select.jsx | 4 +- src/components/custom-slider.jsx | 5 +- src/components/custom-toggle.jsx | 4 +- 16 files changed, 94 insertions(+), 98 deletions(-) diff --git a/src/app/15-puzzle/page.jsx b/src/app/15-puzzle/page.jsx index 2803088..6314fd2 100644 --- a/src/app/15-puzzle/page.jsx +++ b/src/app/15-puzzle/page.jsx @@ -17,17 +17,20 @@ class Puzzle extends Component { squares: times(16, i => ({ value: i })), + isRunning: false, }; } balsal = async () => { + if (this.state.isRunning) return; + this.setState({ isRunning: true }); for (let i = 0; i < 15; i++) { this.setState({ squares: this.state.squares.slice().swap(i, i + 1) }); await sleep(500); } - + this.setState({ isRunning: false }); } render() { @@ -57,6 +60,7 @@ class Puzzle extends Component { diff --git a/src/app/convex-hull/page.jsx b/src/app/convex-hull/page.jsx index 5e89633..bdf5895 100644 --- a/src/app/convex-hull/page.jsx +++ b/src/app/convex-hull/page.jsx @@ -36,6 +36,7 @@ class ConvexHull extends Component { onVisualize={this.handleVisualize} onChangeSpeed={this.changeSpeed} onChangeValues={this.handleValueIncease} + isDisabled={this.state.isRunning} />
@@ -68,7 +69,7 @@ class ConvexHull extends Component { this.setState({ isRunning: true }); } handleTurnOff = () => { - this.setState({ onGoing: false }); + this.setState({ isRunning: false }); } handleRefreshDots = () => { this.setState({ isRunning: false }); diff --git a/src/app/game-of-life/menu.jsx b/src/app/game-of-life/menu.jsx index 348a04d..600d1e0 100644 --- a/src/app/game-of-life/menu.jsx +++ b/src/app/game-of-life/menu.jsx @@ -1,15 +1,14 @@ import { Button } from '@/components/ui/button'; import PropTypes from 'prop-types'; -export default function Menu({ onStart, onStop, onClear }) { +export default function Menu({ onStart, onStop, onClear, isRunning }) { return (

Settings

- - {/* */} - - - + + + +
); } diff --git a/src/app/game-of-life/page.jsx b/src/app/game-of-life/page.jsx index e42c22c..1322794 100644 --- a/src/app/game-of-life/page.jsx +++ b/src/app/game-of-life/page.jsx @@ -84,6 +84,7 @@ export default function GameOfLifePage() { onStart={handleStart} onStop={handleStop} onClear={handleClearBoard} + isRunning={running} />
diff --git a/src/app/pathfinder/menu.jsx b/src/app/pathfinder/menu.jsx index ef599f4..e3a678e 100644 --- a/src/app/pathfinder/menu.jsx +++ b/src/app/pathfinder/menu.jsx @@ -4,24 +4,27 @@ import { Component } from 'react'; class Menu extends Component { render() { + const disabled = this.props.disable; return (

Settings

- + - - - - + + + +
); } diff --git a/src/app/pathfinder/page.jsx b/src/app/pathfinder/page.jsx index 7d0a7b1..eb468cd 100644 --- a/src/app/pathfinder/page.jsx +++ b/src/app/pathfinder/page.jsx @@ -22,7 +22,8 @@ class Pathfinder extends Component { mazes: [ "Recursive division", "Random", "Recursive Horizontal bias(NA)", "Recursive Vertical bias(NA)" ], - maze: 0 + maze: 0, + isRunning: false } this.gridRef = createRef(); } @@ -62,6 +63,7 @@ class Pathfinder extends Component { onCreateMaze={this.handleCreateMaze} onClearBoard={this.handleClearBoard} onClearPath={this.handleClearPath} + disable={this.state.isRunning} />
@@ -107,30 +109,31 @@ class Pathfinder extends Component { this.setState({ maze: val }); } - handleCreateMaze = () => { + handleCreateMaze = async () => { + if (this.state.isRunning) return; + this.setState({ isRunning: true }); + const { row, col, startNode, endNode } = this.state; + const gridCopy = getInitialGrid(row, col); let pairs; switch (this.state.maze) { case 1: - pairs = randomMaze(this.state.grid, this.state.row, this.state.col); + pairs = randomMaze(gridCopy, row, col); break; default: - pairs = getMaze(this.state.grid, this.state.row, this.state.col); + pairs = getMaze(gridCopy, row, col); } - const { startNode, endNode } = this.state; + const grid = this.state.grid; for (let i = 0; i < pairs.length; i++) { - setTimeout(() => { - if (i === pairs.length - 1) { - // this.setState({grid:this.state.grid}); - const grid = this.state.grid; - grid[startNode.row][startNode.col] = { ...grid[startNode.row][startNode.col], isWall: false }; - grid[endNode.row][endNode.col] = { ...grid[endNode.row][endNode.col], isWall: false }; - this.setState({ grid }); - } - if ((pairs[i].xx !== startNode.row || pairs[i].yy !== startNode.col) && (pairs[i].xx !== endNode.row || pairs[i].yy !== endNode.col)) { - document.getElementById(`node-${pairs[i].xx}-${pairs[i].yy}`).className = "node node-wall"; - } - }, i * 20); + const { xx, yy } = pairs[i]; + if ((xx !== startNode.row || yy !== startNode.col) && (xx !== endNode.row || yy !== endNode.col)) { + grid[xx][yy] = { ...grid[xx][yy], isWall: true }; + this.setState({ grid: [...grid] }); + } + await sleep(20); } + grid[startNode.row][startNode.col] = { ...grid[startNode.row][startNode.col], isWall: false }; + grid[endNode.row][endNode.col] = { ...grid[endNode.row][endNode.col], isWall: false }; + this.setState({ grid: [...grid], isRunning: false }); } handleClearBoard = () => { const { grid, row, col } = this.state; @@ -141,11 +144,8 @@ class Pathfinder extends Component { this.setState({ grid: clearPath(grid, row, col) }); } handleClick = () => { - /* for(let i = 0;i<20;i++){ - for(let j = 0; j<50;j++){ - document.getElementById(`node-${i}-${j}`).className = "node"; - } - }*/ + if (this.state.isRunning) return; + this.setState({ isRunning: true }); this.visualizeDijkstra(); /*for(let i = 0;i row.map(node => ({ ...node }))); + const startNode = gridCopy[this.state.startNode.row][this.state.startNode.col]; + const finishNode = gridCopy[this.state.endNode.row][this.state.endNode.col]; let visitedNodesInOrder; switch (this.state.algo) { case 0: - visitedNodesInOrder = dijkstra(grid, startNode, finishNode); + visitedNodesInOrder = dijkstra(gridCopy, startNode, finishNode); break; case 1: - visitedNodesInOrder = aStar(grid, startNode, finishNode); + visitedNodesInOrder = aStar(gridCopy, startNode, finishNode); break; case 2: - visitedNodesInOrder = bfsdfs(grid, startNode, finishNode, "bfs"); + visitedNodesInOrder = bfsdfs(gridCopy, startNode, finishNode, "bfs"); break; default: - visitedNodesInOrder = bfsdfs(grid, startNode, finishNode, "dfs"); + visitedNodesInOrder = bfsdfs(gridCopy, startNode, finishNode, "dfs"); } const nodesInShortestPathOrder = getNodesInShortestPathOrder(finishNode); this.animateDijkstra(visitedNodesInOrder, nodesInShortestPathOrder); } async animateDijkstra(visitedNodesInOrder, nodesInShortestPathOrder) { - for (let i = 0; i <= visitedNodesInOrder.length; i++) { - - if (i === visitedNodesInOrder.length) { - // setTimeout(() => { - await sleep(100); - await this.animateShortestPath(nodesInShortestPathOrder); - - // }, 10 * (i+10)); - return; - } - // setTimeout(() => { + for (let i = 0; i < visitedNodesInOrder.length; i++) { const node = visitedNodesInOrder[i]; - const newGrid = toggleVisit(this.state.grid, node.row, node.col); - //this.setState({grid:newGrid}); - document.getElementById(`node-${node.row}-${node.col}`).className = - 'node node-visited'; + const grid = this.state.grid; + const newNode = { ...grid[node.row][node.col], isVisited: true }; + grid[node.row][node.col] = newNode; + this.setState({ grid: [...grid] }); await sleep(10); - // }, 10 * i); } + await sleep(100); + await this.animateShortestPath(nodesInShortestPathOrder); } async animateShortestPath(nodesInShortestPathOrder) { - const grid = this.state.grid; - const newGrid = grid.slice(); for (let i = 0; i < nodesInShortestPathOrder.length; i++) { - // setTimeout(() => { - const node = nodesInShortestPathOrder[i]; - const newNode = { ...newGrid[node.row][node.col], ispathNode: true }; - newGrid[node.row][node.col] = newNode; - if (i === nodesInShortestPathOrder.length - 1) { - this.setState({ grid: newGrid }); - } - document.getElementById(`node-${node.row}-${node.col}`).className = - 'node node-shortest-path'; + const grid = this.state.grid; + const newNode = { ...grid[node.row][node.col], ispathNode: true }; + grid[node.row][node.col] = newNode; + this.setState({ grid: [...grid] }); await sleep(50); - //}, 50 * i); } + this.setState({ isRunning: false }); } } @@ -262,16 +247,6 @@ const clearBoard = (grid, row, col) => { return newGrid; } -const toggleVisit = (grid, row, col) => { - const newGrid = grid.slice(); - const node = newGrid[row][col]; - const newNode = { - ...node, - visitedNode: !node.visitedNode - }; - newGrid[row][col] = newNode; - return newGrid; -} const getNewGridWithWallToggled = (grid, row, col) => { const newGrid = grid.slice(); const node = newGrid[row][col]; diff --git a/src/app/prime-numbers/menu.jsx b/src/app/prime-numbers/menu.jsx index 0a88ffc..a913495 100644 --- a/src/app/prime-numbers/menu.jsx +++ b/src/app/prime-numbers/menu.jsx @@ -13,6 +13,7 @@ class Menu extends Component { title="Select Algorithm" options={["Sieve", "Spiral"]} onChange={this.props.setAlgo} + disabled={this.props.isDisabled} />
@@ -28,6 +30,7 @@ class Menu extends Component { aria-label="Username" aria-describedby="basic-addon1" onChange={this.props.setInput2} + disabled={this.props.disable} />
diff --git a/src/app/turing-machine/page.jsx b/src/app/turing-machine/page.jsx index 8dc8c57..32e3e9f 100644 --- a/src/app/turing-machine/page.jsx +++ b/src/app/turing-machine/page.jsx @@ -22,7 +22,8 @@ class TuringMachine extends Component { inputString2: "", table: [], algo: 0, - state: -1 + state: -1, + isRunning: false } } @@ -76,6 +77,8 @@ class TuringMachine extends Component { } handleStart = () => { + if (this.state.isRunning) return; + this.setState({ isRunning: true }); this.handleSet(); this.handleAlgo(); } @@ -105,6 +108,7 @@ class TuringMachine extends Component { state = nextState; } + this.setState({ isRunning: false }); } setInput1 = (event) => { @@ -133,6 +137,7 @@ class TuringMachine extends Component { onReset={this.handleReset} setInput1={this.setInput1} setInput2={this.setInput2} + disable={this.state.isRunning} />
diff --git a/src/components/custom-select.jsx b/src/components/custom-select.jsx index 534f942..486b179 100644 --- a/src/components/custom-select.jsx +++ b/src/components/custom-select.jsx @@ -9,7 +9,7 @@ import { -export function CustomSelect({ title, options, onChange }) { +export function CustomSelect({ title, options, onChange, disabled }) { const [value, setValue] = React.useState(0) const onChangeCover = (value) => { setValue(value) @@ -19,7 +19,7 @@ export function CustomSelect({ title, options, onChange }) {
{/*
*/} - {/* */} diff --git a/src/components/custom-slider.jsx b/src/components/custom-slider.jsx index 82bed58..12e8e37 100644 --- a/src/components/custom-slider.jsx +++ b/src/components/custom-slider.jsx @@ -2,7 +2,8 @@ import * as React from "react" import { Slider } from "@/components/ui/slider" -export function CustomSlider({ title, onChange, min, max, step, defaultValue }) { +export function CustomSlider({ title, onChange, min, max, step, defaultValue, disable, isDisabled }) { + const disabled = disable || isDisabled || false; const [value, setValue] = React.useState(defaultValue) const onChangeCover = (value) => { setValue(value) @@ -20,8 +21,8 @@ export function CustomSlider({ title, onChange, min, max, step, defaultValue }) min={min} max={max} step={step} + disabled={disabled} className="w-full" - // className="w-[180px]" /> {value}
diff --git a/src/components/custom-toggle.jsx b/src/components/custom-toggle.jsx index 2c2e559..31e0848 100644 --- a/src/components/custom-toggle.jsx +++ b/src/components/custom-toggle.jsx @@ -7,7 +7,7 @@ import { Switch } from "@/components/ui/switch" // onCheckedChange: (checked: boolean) => void // } -export function CustomToggle({ title, onCheckedChange }) { +export function CustomToggle({ title, onCheckedChange, disabled }) { const [checked, setChecked] = React.useState(false) const onCheckedChangeCover = (checked) => { setChecked(checked) @@ -15,7 +15,7 @@ export function CustomToggle({ title, onCheckedChange }) { } return (
- +