Skip to content
Snippets Groups Projects
SortingVisualizer.jsx 7.52 KiB
Newer Older
  • Learn to ignore specific revisions
  • import {useState, useEffect} from 'react';
    
    Daniel's avatar
    Daniel committed
    import './SortingVisualizer.css'
    
    Daniel's avatar
    Daniel committed
    import BubbleSort, {abortBubbleSort} from "./BubbleSort";
    import startMergeSort, {abortMergeSort} from "./MergeSort.js";
    
    Daniel's avatar
    Daniel committed
    import RangeSlider from 'react-bootstrap-range-slider';
    import 'react-bootstrap-range-slider/dist/react-bootstrap-range-slider.css';
    
    Daniel's avatar
    Daniel committed
    import {Dropdown} from 'react-bootstrap';
    import 'bootstrap/dist/css/bootstrap.min.css';
    
    Daniel's avatar
    Daniel committed
    
    
    Daniel's avatar
    Daniel committed
    
    
    Daniel's avatar
    Daniel committed
    function SortingVisualizer() {
    
    
    Daniel's avatar
    Daniel committed
        const moduloFive = ((window.innerWidth / 45)).toFixed(0) % 5;
        const monitorSize = (window.innerWidth / 45).toFixed(0) - moduloFive;
        const [BarNumber, setBarNumber] = useState(() => monitorSize > 100 ? 100 : monitorSize); // max of 100 bars
    
    Daniel's avatar
    Daniel committed
        const [bars, setbars] = useState(() => generateArray(BarNumber));
        const [sorting, setSorting] = useState(false);
    
    Daniel's avatar
    Daniel committed
        const [alreadySorted, setColor] = useState();
    
    Daniel's avatar
    Daniel committed
        const [isSorted, setIsSorted] = useState(false);
    
        const [sortSpeed, setSortSpeed] = useState('40');
    
        const [Algo, setAlgo] = useState('MergeSort');
    
    Daniel's avatar
    Daniel committed
    
        const SliderWithInputFormControl = () => {
            const [sliderValue_intern, setSliderValue_intern] = useState(BarNumber);
            return (
                <RangeSlider
                    value={sliderValue_intern}
                    step={5}
                    max={100}
                    min={5}
                    disabled={sorting}
                    onChange={changeEvent => setSliderValue_intern(Number(changeEvent.target.value))}
                    onAfterChange={() => setBarNumber(sliderValue_intern)}
                />
            );
        };
    
        useEffect(() => {
            resetBars();
        }, [BarNumber]);
    
        function getRandomArbitrary(min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
    
        function generateArray(numberBars) {
    
    Daniel's avatar
    Daniel committed
            return Array.from({length: numberBars}, () => getRandomArbitrary(20, 450));
    
    Daniel's avatar
    Daniel committed
        }
    
        async function handleSorting() {
            if (sorting) return;
            setSorting(true);
    
    Daniel's avatar
    Daniel committed
            if (Algo == 'BubbleSort') {
                await BubbleSort(bars, setbars, setColor, sortSpeed);
            } else if (Algo == 'MergeSort') {
                await startMergeSort(bars, setbars, setColor, sortSpeed);
            }
    
    Daniel's avatar
    Daniel committed
            setSorting(false);
        }
    
        function resetBars() {
    
    Daniel's avatar
    Daniel committed
            setColor(1000);
    
    Daniel's avatar
    Daniel committed
            setbars(generateArray(BarNumber));
            let ColoredBars = document.getElementsByClassName('sorted');
    
    Daniel's avatar
    Daniel committed
            document.getElementById('startButton').innerText = 'Sort!';
    
    Daniel's avatar
    Daniel committed
            while (ColoredBars.length > 0) {
                ColoredBars[0].className = 'bar';
            }
            setIsSorted(false);
    
    
        }
    
        function finishAnimation() {
            let ColoredBars = document.getElementsByClassName('sorted');
            while (ColoredBars.length > 0) {
                ColoredBars[0].className = 'finished';
            }
        }
    
        function handleButton() {
            if (!sorting && !isSorted) {
    
    Daniel's avatar
    Daniel committed
                setColor(1000);
    
    Daniel's avatar
    Daniel committed
                handleSorting();
            } else if (isSorted) {
                resetBars();
    
            }
        }
    
    
    Daniel's avatar
    Daniel committed
        async function stopSorting() {
            switch (Algo) {
                case 'BubbleSort':
                    abortBubbleSort();
                    break;
                case 'MergeSort':
                    abortMergeSort();
                    break;
            }
            await new Promise(resolve => setTimeout(resolve, 200));
            document.getElementById('startButton').innerText = 'Reset!';
            setIsSorted(true);
        }
    
    
    Daniel's avatar
    Daniel committed
        useEffect(() => {
            if (alreadySorted == 0) {
                setIsSorted(true);
                finishAnimation();
    
    Daniel's avatar
    Daniel committed
                document.getElementById('startButton').innerText = 'Reset!';
    
    Daniel's avatar
    Daniel committed
            }
        }, [alreadySorted]);
    
    
    Daniel's avatar
    Daniel committed
    
    
    Daniel's avatar
    Daniel committed
        return (
            <>
                <div className='Container'>
    
                    <div id='ToolBar'>
    
    Daniel's avatar
    Daniel committed
                        <div className="Label">How many bars?</div>
    
    Daniel's avatar
    Daniel committed
                        <SliderWithInputFormControl style={{className: 'range-slider'}}/>
    
    Daniel's avatar
    Daniel committed
                        <input type='number' max={100} min={0} disabled={sorting}
    
    Daniel's avatar
    Daniel committed
                               onChange={changeEvent => {
                                   let value = Number(changeEvent.target.value);
                                   if (value > 100) value = 100;
                                   if (value < 5) value = 5;
                                   setBarNumber(value);
                               }} defaultValue={BarNumber}
    
    Daniel's avatar
    Daniel committed
                               value={BarNumber} style={{
    
    Daniel's avatar
    Daniel committed
                            width: '46px',
    
    Daniel's avatar
    Daniel committed
                            fontfamily: 'Arial sans-serif',
    
    Daniel's avatar
    Daniel committed
                            fontSize: '16px',
                            marginLeft: '10px',
                            marginRight: '5px',
    
    Daniel's avatar
    Daniel committed
                            borderRadius: '5px',
                            border: 'none',
                            textAlign: 'center'
                        }}/>
    
    Daniel's avatar
    Daniel committed
                        <div id="dividerLeft" className="Divider"></div>
                        <Dropdown id="dropdown" className="dropdown-wide">
    
    Daniel's avatar
    Daniel committed
                            <Dropdown.Toggle
                                id="dropdown"
                                disabled={sorting}
                            >
                                Sorting-Speed: {
                                {
                                    100: 'Slow',
                                    40: 'Medium',
                                    0: 'Fast'
                                }[sortSpeed] || ''
                            }
                            </Dropdown.Toggle>
                            <Dropdown.Menu id="dropdown-menu">
                                <Dropdown.Item id="dropdown-item" onClick={() => setSortSpeed(100)}>
                                    Slow
                                </Dropdown.Item>
                                <Dropdown.Item id="dropdown-item" onClick={() => setSortSpeed(40)}>
                                    Medium
                                </Dropdown.Item>
                                <Dropdown.Item id="dropdown-item" onClick={() => setSortSpeed(0)}>
                                    Fast
                                </Dropdown.Item>
                            </Dropdown.Menu>
                        </Dropdown>
    
    Daniel's avatar
    Daniel committed
                        <div className="Divider"></div>
                        <Dropdown id="dropdown">
                            <Dropdown.Toggle
                                id="dropdown"
                                disabled={sorting}
                            >
                                Algorithm: {
                                [Algo]
                            }
                            </Dropdown.Toggle>
                            <Dropdown.Menu id="dropdown-menu">
                                <Dropdown.Item id="dropdown-item" onClick={() => setAlgo('BubbleSort')}>
                                    BubbleSort
                                </Dropdown.Item>
                                <Dropdown.Item id="dropdown-item" onClick={() => setAlgo('MergeSort')}>
                                    MergeSort
                                </Dropdown.Item>
                            </Dropdown.Menu>
                        </Dropdown>
    
    
    Daniel's avatar
    Daniel committed
                        <button id='startButton' className="Button"
    
    Daniel's avatar
    Daniel committed
                                style={{background: `${sorting ? 'red' : ''}`}} onClick={() => {
    
    Daniel's avatar
    Daniel committed
                            sorting ? stopSorting() : handleButton()
    
    Daniel's avatar
    Daniel committed
                        }}>{sorting ? 'Sorting...' : 'Sort!'}</button>
    
    Daniel's avatar
    Daniel committed
                    </div>
                    <div className='DivArray'>
                        {bars.map((height, index) => (<div key={index} data-key={index}
                                                           className={`bar + ${alreadySorted <= index ? 'sorted' : ''}`}
                                                           style={{height: `${height}px`}}></div>))}
                    </div>
    
                </div>
            </>
        )
    };
    export default SortingVisualizer;