├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── public
├── favicon.png
└── swapsound.mp3
└── src
├── _reset.scss
├── algorithms
├── BubbleSort.js
├── BucketSort.js
├── HeapSort.js
├── InsertionSort.js
├── MergeSort.js
├── Quicksort.js
├── RadixSort.js
└── SelectionSort.js
├── components
└── Bar.js
├── main.js
├── style.scss
└── utils
├── DisplayBars.js
├── IsSorted.js
├── Shuffle.js
└── SoundManager.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sorting Visualizer
2 |
3 | Sorting algorithm visualization tool to illustrate and compare the mechanics and efficiency of various sorting algorithms in real-time. This tool allows you to change the algorithm, adjust the number of items in the dataset between 1 and 1,000 and change the speed at which the algorithm executes between 1ms and 1000ms.
4 |
5 | ## Live Demo
6 |
7 | [View Live Site Here](https://tylermommsen-sorting-visualizer.vercel.app/)
8 |
9 | ## Built With
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ## Sorting Algorithms
20 |
21 | - **Quicksort** - Selects a 'pivot' element from the array and partitions the other elements into two sub-arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then sorted recursively.
22 |
23 | - **Bubble Sort** - Repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. The pass through the list is repeated until the list is sorted.
24 |
25 | - **Insertion Sort** - Builds the final sorted array (or list) one item at a time. It is much less efficient on large lists than more advanced algorithms such as quicksort, heapsort, or merge sort.
26 |
27 | - **Merge Sort** - A divide and conquer algorithm that divides the input array into two halves, calls itself for the two halves, and then merges the two sorted halves.
28 |
29 | - **Selection Sort** - Divides the input list into two parts: a sorted sublist of items which is built up from left to right at the front (left) of the list, and a sublist of the remaining unsorted items. The algorithm proceeds by finding the smallest (or largest, depending on sorting order) element in the unsorted sublist, exchanging it with the leftmost unsorted element, and moving the sublist boundaries one element to the right.
30 |
31 | - **Radix Sort** - Sorts numbers digit by digit, starting from the least significant digit to the most significant digit. Radix sort uses counting sort as a subroutine to sort.
32 |
33 | - **Bucket Sort** - Distributes the elements of an array into a number of buckets. Each bucket is then sorted individually, either using a different sorting algorithm, or by recursively applying the bucket sort algorithm.
34 |
35 | - **Heap Sort** - A hybrid stable sorting algorithm derived from merge sort and insertion sort, designed to perform well on many kinds of real-world data.
36 |
37 | ## Gif
38 | 
39 |
40 | ## Development
41 |
42 | Follow these steps to run the project locally.
43 |
44 | 1. Clone the repository.
45 |
46 | HTTPS
47 |
48 | ```sh
49 | git clone https://github.com/TylerMommsen/pathfinding-visualizer.git
50 | ```
51 |
52 | SSH
53 |
54 | ```sh
55 | git clone git@github.com:TylerMommsen/pathfinding-visualizer.git
56 | ```
57 |
58 | 2. Install dependenices
59 |
60 | ```sh
61 | npm install
62 | ```
63 |
64 | 3. Run the project
65 |
66 | ```sh
67 | npm run dev
68 | ```
69 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Sorting Visualizer
8 |
9 |
10 |
11 |
12 |
13 | Sorting Visualizer
14 |
15 |
16 |
17 |
18 |
19 |
20 |
Quicksort
21 |
29 |
34 |
35 |
36 |
37 |
38 |
Quicksort
39 |
Bubble Sort
40 |
Insertion Sort
41 |
Merge Sort
42 |
Selection Sort
43 |
Radix Sort
44 |
Bucket Sort
45 |
Heap Sort
46 |
47 |
48 |
49 |
Visualize Quicksort
50 |
Shuffle
51 |
60 |
65 |
66 |
74 |
79 |
80 |
81 |
82 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sorting-visualizer",
3 | "version": "0.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "sorting-visualizer",
9 | "version": "0.0.0",
10 | "devDependencies": {
11 | "sass": "^1.74.1",
12 | "vite": "^5.2.0"
13 | }
14 | },
15 | "node_modules/@esbuild/aix-ppc64": {
16 | "version": "0.20.2",
17 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
18 | "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
19 | "cpu": [
20 | "ppc64"
21 | ],
22 | "dev": true,
23 | "optional": true,
24 | "os": [
25 | "aix"
26 | ],
27 | "engines": {
28 | "node": ">=12"
29 | }
30 | },
31 | "node_modules/@esbuild/android-arm": {
32 | "version": "0.20.2",
33 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
34 | "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
35 | "cpu": [
36 | "arm"
37 | ],
38 | "dev": true,
39 | "optional": true,
40 | "os": [
41 | "android"
42 | ],
43 | "engines": {
44 | "node": ">=12"
45 | }
46 | },
47 | "node_modules/@esbuild/android-arm64": {
48 | "version": "0.20.2",
49 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
50 | "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
51 | "cpu": [
52 | "arm64"
53 | ],
54 | "dev": true,
55 | "optional": true,
56 | "os": [
57 | "android"
58 | ],
59 | "engines": {
60 | "node": ">=12"
61 | }
62 | },
63 | "node_modules/@esbuild/android-x64": {
64 | "version": "0.20.2",
65 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
66 | "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
67 | "cpu": [
68 | "x64"
69 | ],
70 | "dev": true,
71 | "optional": true,
72 | "os": [
73 | "android"
74 | ],
75 | "engines": {
76 | "node": ">=12"
77 | }
78 | },
79 | "node_modules/@esbuild/darwin-arm64": {
80 | "version": "0.20.2",
81 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
82 | "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
83 | "cpu": [
84 | "arm64"
85 | ],
86 | "dev": true,
87 | "optional": true,
88 | "os": [
89 | "darwin"
90 | ],
91 | "engines": {
92 | "node": ">=12"
93 | }
94 | },
95 | "node_modules/@esbuild/darwin-x64": {
96 | "version": "0.20.2",
97 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
98 | "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
99 | "cpu": [
100 | "x64"
101 | ],
102 | "dev": true,
103 | "optional": true,
104 | "os": [
105 | "darwin"
106 | ],
107 | "engines": {
108 | "node": ">=12"
109 | }
110 | },
111 | "node_modules/@esbuild/freebsd-arm64": {
112 | "version": "0.20.2",
113 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
114 | "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
115 | "cpu": [
116 | "arm64"
117 | ],
118 | "dev": true,
119 | "optional": true,
120 | "os": [
121 | "freebsd"
122 | ],
123 | "engines": {
124 | "node": ">=12"
125 | }
126 | },
127 | "node_modules/@esbuild/freebsd-x64": {
128 | "version": "0.20.2",
129 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
130 | "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
131 | "cpu": [
132 | "x64"
133 | ],
134 | "dev": true,
135 | "optional": true,
136 | "os": [
137 | "freebsd"
138 | ],
139 | "engines": {
140 | "node": ">=12"
141 | }
142 | },
143 | "node_modules/@esbuild/linux-arm": {
144 | "version": "0.20.2",
145 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
146 | "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
147 | "cpu": [
148 | "arm"
149 | ],
150 | "dev": true,
151 | "optional": true,
152 | "os": [
153 | "linux"
154 | ],
155 | "engines": {
156 | "node": ">=12"
157 | }
158 | },
159 | "node_modules/@esbuild/linux-arm64": {
160 | "version": "0.20.2",
161 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
162 | "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
163 | "cpu": [
164 | "arm64"
165 | ],
166 | "dev": true,
167 | "optional": true,
168 | "os": [
169 | "linux"
170 | ],
171 | "engines": {
172 | "node": ">=12"
173 | }
174 | },
175 | "node_modules/@esbuild/linux-ia32": {
176 | "version": "0.20.2",
177 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
178 | "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
179 | "cpu": [
180 | "ia32"
181 | ],
182 | "dev": true,
183 | "optional": true,
184 | "os": [
185 | "linux"
186 | ],
187 | "engines": {
188 | "node": ">=12"
189 | }
190 | },
191 | "node_modules/@esbuild/linux-loong64": {
192 | "version": "0.20.2",
193 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
194 | "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
195 | "cpu": [
196 | "loong64"
197 | ],
198 | "dev": true,
199 | "optional": true,
200 | "os": [
201 | "linux"
202 | ],
203 | "engines": {
204 | "node": ">=12"
205 | }
206 | },
207 | "node_modules/@esbuild/linux-mips64el": {
208 | "version": "0.20.2",
209 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
210 | "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
211 | "cpu": [
212 | "mips64el"
213 | ],
214 | "dev": true,
215 | "optional": true,
216 | "os": [
217 | "linux"
218 | ],
219 | "engines": {
220 | "node": ">=12"
221 | }
222 | },
223 | "node_modules/@esbuild/linux-ppc64": {
224 | "version": "0.20.2",
225 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
226 | "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
227 | "cpu": [
228 | "ppc64"
229 | ],
230 | "dev": true,
231 | "optional": true,
232 | "os": [
233 | "linux"
234 | ],
235 | "engines": {
236 | "node": ">=12"
237 | }
238 | },
239 | "node_modules/@esbuild/linux-riscv64": {
240 | "version": "0.20.2",
241 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
242 | "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
243 | "cpu": [
244 | "riscv64"
245 | ],
246 | "dev": true,
247 | "optional": true,
248 | "os": [
249 | "linux"
250 | ],
251 | "engines": {
252 | "node": ">=12"
253 | }
254 | },
255 | "node_modules/@esbuild/linux-s390x": {
256 | "version": "0.20.2",
257 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
258 | "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
259 | "cpu": [
260 | "s390x"
261 | ],
262 | "dev": true,
263 | "optional": true,
264 | "os": [
265 | "linux"
266 | ],
267 | "engines": {
268 | "node": ">=12"
269 | }
270 | },
271 | "node_modules/@esbuild/linux-x64": {
272 | "version": "0.20.2",
273 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
274 | "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
275 | "cpu": [
276 | "x64"
277 | ],
278 | "dev": true,
279 | "optional": true,
280 | "os": [
281 | "linux"
282 | ],
283 | "engines": {
284 | "node": ">=12"
285 | }
286 | },
287 | "node_modules/@esbuild/netbsd-x64": {
288 | "version": "0.20.2",
289 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
290 | "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
291 | "cpu": [
292 | "x64"
293 | ],
294 | "dev": true,
295 | "optional": true,
296 | "os": [
297 | "netbsd"
298 | ],
299 | "engines": {
300 | "node": ">=12"
301 | }
302 | },
303 | "node_modules/@esbuild/openbsd-x64": {
304 | "version": "0.20.2",
305 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
306 | "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
307 | "cpu": [
308 | "x64"
309 | ],
310 | "dev": true,
311 | "optional": true,
312 | "os": [
313 | "openbsd"
314 | ],
315 | "engines": {
316 | "node": ">=12"
317 | }
318 | },
319 | "node_modules/@esbuild/sunos-x64": {
320 | "version": "0.20.2",
321 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
322 | "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
323 | "cpu": [
324 | "x64"
325 | ],
326 | "dev": true,
327 | "optional": true,
328 | "os": [
329 | "sunos"
330 | ],
331 | "engines": {
332 | "node": ">=12"
333 | }
334 | },
335 | "node_modules/@esbuild/win32-arm64": {
336 | "version": "0.20.2",
337 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
338 | "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
339 | "cpu": [
340 | "arm64"
341 | ],
342 | "dev": true,
343 | "optional": true,
344 | "os": [
345 | "win32"
346 | ],
347 | "engines": {
348 | "node": ">=12"
349 | }
350 | },
351 | "node_modules/@esbuild/win32-ia32": {
352 | "version": "0.20.2",
353 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
354 | "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
355 | "cpu": [
356 | "ia32"
357 | ],
358 | "dev": true,
359 | "optional": true,
360 | "os": [
361 | "win32"
362 | ],
363 | "engines": {
364 | "node": ">=12"
365 | }
366 | },
367 | "node_modules/@esbuild/win32-x64": {
368 | "version": "0.20.2",
369 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
370 | "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
371 | "cpu": [
372 | "x64"
373 | ],
374 | "dev": true,
375 | "optional": true,
376 | "os": [
377 | "win32"
378 | ],
379 | "engines": {
380 | "node": ">=12"
381 | }
382 | },
383 | "node_modules/@rollup/rollup-android-arm-eabi": {
384 | "version": "4.14.0",
385 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz",
386 | "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==",
387 | "cpu": [
388 | "arm"
389 | ],
390 | "dev": true,
391 | "optional": true,
392 | "os": [
393 | "android"
394 | ]
395 | },
396 | "node_modules/@rollup/rollup-android-arm64": {
397 | "version": "4.14.0",
398 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz",
399 | "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==",
400 | "cpu": [
401 | "arm64"
402 | ],
403 | "dev": true,
404 | "optional": true,
405 | "os": [
406 | "android"
407 | ]
408 | },
409 | "node_modules/@rollup/rollup-darwin-arm64": {
410 | "version": "4.14.0",
411 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz",
412 | "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==",
413 | "cpu": [
414 | "arm64"
415 | ],
416 | "dev": true,
417 | "optional": true,
418 | "os": [
419 | "darwin"
420 | ]
421 | },
422 | "node_modules/@rollup/rollup-darwin-x64": {
423 | "version": "4.14.0",
424 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz",
425 | "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==",
426 | "cpu": [
427 | "x64"
428 | ],
429 | "dev": true,
430 | "optional": true,
431 | "os": [
432 | "darwin"
433 | ]
434 | },
435 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
436 | "version": "4.14.0",
437 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz",
438 | "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==",
439 | "cpu": [
440 | "arm"
441 | ],
442 | "dev": true,
443 | "optional": true,
444 | "os": [
445 | "linux"
446 | ]
447 | },
448 | "node_modules/@rollup/rollup-linux-arm64-gnu": {
449 | "version": "4.14.0",
450 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz",
451 | "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==",
452 | "cpu": [
453 | "arm64"
454 | ],
455 | "dev": true,
456 | "optional": true,
457 | "os": [
458 | "linux"
459 | ]
460 | },
461 | "node_modules/@rollup/rollup-linux-arm64-musl": {
462 | "version": "4.14.0",
463 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz",
464 | "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==",
465 | "cpu": [
466 | "arm64"
467 | ],
468 | "dev": true,
469 | "optional": true,
470 | "os": [
471 | "linux"
472 | ]
473 | },
474 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
475 | "version": "4.14.0",
476 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz",
477 | "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==",
478 | "cpu": [
479 | "ppc64le"
480 | ],
481 | "dev": true,
482 | "optional": true,
483 | "os": [
484 | "linux"
485 | ]
486 | },
487 | "node_modules/@rollup/rollup-linux-riscv64-gnu": {
488 | "version": "4.14.0",
489 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz",
490 | "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==",
491 | "cpu": [
492 | "riscv64"
493 | ],
494 | "dev": true,
495 | "optional": true,
496 | "os": [
497 | "linux"
498 | ]
499 | },
500 | "node_modules/@rollup/rollup-linux-s390x-gnu": {
501 | "version": "4.14.0",
502 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz",
503 | "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==",
504 | "cpu": [
505 | "s390x"
506 | ],
507 | "dev": true,
508 | "optional": true,
509 | "os": [
510 | "linux"
511 | ]
512 | },
513 | "node_modules/@rollup/rollup-linux-x64-gnu": {
514 | "version": "4.14.0",
515 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz",
516 | "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==",
517 | "cpu": [
518 | "x64"
519 | ],
520 | "dev": true,
521 | "optional": true,
522 | "os": [
523 | "linux"
524 | ]
525 | },
526 | "node_modules/@rollup/rollup-linux-x64-musl": {
527 | "version": "4.14.0",
528 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz",
529 | "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==",
530 | "cpu": [
531 | "x64"
532 | ],
533 | "dev": true,
534 | "optional": true,
535 | "os": [
536 | "linux"
537 | ]
538 | },
539 | "node_modules/@rollup/rollup-win32-arm64-msvc": {
540 | "version": "4.14.0",
541 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz",
542 | "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==",
543 | "cpu": [
544 | "arm64"
545 | ],
546 | "dev": true,
547 | "optional": true,
548 | "os": [
549 | "win32"
550 | ]
551 | },
552 | "node_modules/@rollup/rollup-win32-ia32-msvc": {
553 | "version": "4.14.0",
554 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz",
555 | "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==",
556 | "cpu": [
557 | "ia32"
558 | ],
559 | "dev": true,
560 | "optional": true,
561 | "os": [
562 | "win32"
563 | ]
564 | },
565 | "node_modules/@rollup/rollup-win32-x64-msvc": {
566 | "version": "4.14.0",
567 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz",
568 | "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==",
569 | "cpu": [
570 | "x64"
571 | ],
572 | "dev": true,
573 | "optional": true,
574 | "os": [
575 | "win32"
576 | ]
577 | },
578 | "node_modules/@types/estree": {
579 | "version": "1.0.5",
580 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
581 | "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
582 | "dev": true
583 | },
584 | "node_modules/anymatch": {
585 | "version": "3.1.3",
586 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
587 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
588 | "dev": true,
589 | "dependencies": {
590 | "normalize-path": "^3.0.0",
591 | "picomatch": "^2.0.4"
592 | },
593 | "engines": {
594 | "node": ">= 8"
595 | }
596 | },
597 | "node_modules/binary-extensions": {
598 | "version": "2.3.0",
599 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
600 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
601 | "dev": true,
602 | "engines": {
603 | "node": ">=8"
604 | },
605 | "funding": {
606 | "url": "https://github.com/sponsors/sindresorhus"
607 | }
608 | },
609 | "node_modules/braces": {
610 | "version": "3.0.2",
611 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
612 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
613 | "dev": true,
614 | "dependencies": {
615 | "fill-range": "^7.0.1"
616 | },
617 | "engines": {
618 | "node": ">=8"
619 | }
620 | },
621 | "node_modules/chokidar": {
622 | "version": "3.6.0",
623 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
624 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
625 | "dev": true,
626 | "dependencies": {
627 | "anymatch": "~3.1.2",
628 | "braces": "~3.0.2",
629 | "glob-parent": "~5.1.2",
630 | "is-binary-path": "~2.1.0",
631 | "is-glob": "~4.0.1",
632 | "normalize-path": "~3.0.0",
633 | "readdirp": "~3.6.0"
634 | },
635 | "engines": {
636 | "node": ">= 8.10.0"
637 | },
638 | "funding": {
639 | "url": "https://paulmillr.com/funding/"
640 | },
641 | "optionalDependencies": {
642 | "fsevents": "~2.3.2"
643 | }
644 | },
645 | "node_modules/esbuild": {
646 | "version": "0.20.2",
647 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
648 | "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
649 | "dev": true,
650 | "hasInstallScript": true,
651 | "bin": {
652 | "esbuild": "bin/esbuild"
653 | },
654 | "engines": {
655 | "node": ">=12"
656 | },
657 | "optionalDependencies": {
658 | "@esbuild/aix-ppc64": "0.20.2",
659 | "@esbuild/android-arm": "0.20.2",
660 | "@esbuild/android-arm64": "0.20.2",
661 | "@esbuild/android-x64": "0.20.2",
662 | "@esbuild/darwin-arm64": "0.20.2",
663 | "@esbuild/darwin-x64": "0.20.2",
664 | "@esbuild/freebsd-arm64": "0.20.2",
665 | "@esbuild/freebsd-x64": "0.20.2",
666 | "@esbuild/linux-arm": "0.20.2",
667 | "@esbuild/linux-arm64": "0.20.2",
668 | "@esbuild/linux-ia32": "0.20.2",
669 | "@esbuild/linux-loong64": "0.20.2",
670 | "@esbuild/linux-mips64el": "0.20.2",
671 | "@esbuild/linux-ppc64": "0.20.2",
672 | "@esbuild/linux-riscv64": "0.20.2",
673 | "@esbuild/linux-s390x": "0.20.2",
674 | "@esbuild/linux-x64": "0.20.2",
675 | "@esbuild/netbsd-x64": "0.20.2",
676 | "@esbuild/openbsd-x64": "0.20.2",
677 | "@esbuild/sunos-x64": "0.20.2",
678 | "@esbuild/win32-arm64": "0.20.2",
679 | "@esbuild/win32-ia32": "0.20.2",
680 | "@esbuild/win32-x64": "0.20.2"
681 | }
682 | },
683 | "node_modules/fill-range": {
684 | "version": "7.0.1",
685 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
686 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
687 | "dev": true,
688 | "dependencies": {
689 | "to-regex-range": "^5.0.1"
690 | },
691 | "engines": {
692 | "node": ">=8"
693 | }
694 | },
695 | "node_modules/fsevents": {
696 | "version": "2.3.3",
697 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
698 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
699 | "dev": true,
700 | "hasInstallScript": true,
701 | "optional": true,
702 | "os": [
703 | "darwin"
704 | ],
705 | "engines": {
706 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
707 | }
708 | },
709 | "node_modules/glob-parent": {
710 | "version": "5.1.2",
711 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
712 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
713 | "dev": true,
714 | "dependencies": {
715 | "is-glob": "^4.0.1"
716 | },
717 | "engines": {
718 | "node": ">= 6"
719 | }
720 | },
721 | "node_modules/immutable": {
722 | "version": "4.3.5",
723 | "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
724 | "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
725 | "dev": true
726 | },
727 | "node_modules/is-binary-path": {
728 | "version": "2.1.0",
729 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
730 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
731 | "dev": true,
732 | "dependencies": {
733 | "binary-extensions": "^2.0.0"
734 | },
735 | "engines": {
736 | "node": ">=8"
737 | }
738 | },
739 | "node_modules/is-extglob": {
740 | "version": "2.1.1",
741 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
742 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
743 | "dev": true,
744 | "engines": {
745 | "node": ">=0.10.0"
746 | }
747 | },
748 | "node_modules/is-glob": {
749 | "version": "4.0.3",
750 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
751 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
752 | "dev": true,
753 | "dependencies": {
754 | "is-extglob": "^2.1.1"
755 | },
756 | "engines": {
757 | "node": ">=0.10.0"
758 | }
759 | },
760 | "node_modules/is-number": {
761 | "version": "7.0.0",
762 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
763 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
764 | "dev": true,
765 | "engines": {
766 | "node": ">=0.12.0"
767 | }
768 | },
769 | "node_modules/nanoid": {
770 | "version": "3.3.7",
771 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
772 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
773 | "dev": true,
774 | "funding": [
775 | {
776 | "type": "github",
777 | "url": "https://github.com/sponsors/ai"
778 | }
779 | ],
780 | "bin": {
781 | "nanoid": "bin/nanoid.cjs"
782 | },
783 | "engines": {
784 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
785 | }
786 | },
787 | "node_modules/normalize-path": {
788 | "version": "3.0.0",
789 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
790 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
791 | "dev": true,
792 | "engines": {
793 | "node": ">=0.10.0"
794 | }
795 | },
796 | "node_modules/picocolors": {
797 | "version": "1.0.0",
798 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
799 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
800 | "dev": true
801 | },
802 | "node_modules/picomatch": {
803 | "version": "2.3.1",
804 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
805 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
806 | "dev": true,
807 | "engines": {
808 | "node": ">=8.6"
809 | },
810 | "funding": {
811 | "url": "https://github.com/sponsors/jonschlinkert"
812 | }
813 | },
814 | "node_modules/postcss": {
815 | "version": "8.4.38",
816 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
817 | "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
818 | "dev": true,
819 | "funding": [
820 | {
821 | "type": "opencollective",
822 | "url": "https://opencollective.com/postcss/"
823 | },
824 | {
825 | "type": "tidelift",
826 | "url": "https://tidelift.com/funding/github/npm/postcss"
827 | },
828 | {
829 | "type": "github",
830 | "url": "https://github.com/sponsors/ai"
831 | }
832 | ],
833 | "dependencies": {
834 | "nanoid": "^3.3.7",
835 | "picocolors": "^1.0.0",
836 | "source-map-js": "^1.2.0"
837 | },
838 | "engines": {
839 | "node": "^10 || ^12 || >=14"
840 | }
841 | },
842 | "node_modules/readdirp": {
843 | "version": "3.6.0",
844 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
845 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
846 | "dev": true,
847 | "dependencies": {
848 | "picomatch": "^2.2.1"
849 | },
850 | "engines": {
851 | "node": ">=8.10.0"
852 | }
853 | },
854 | "node_modules/rollup": {
855 | "version": "4.14.0",
856 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz",
857 | "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==",
858 | "dev": true,
859 | "dependencies": {
860 | "@types/estree": "1.0.5"
861 | },
862 | "bin": {
863 | "rollup": "dist/bin/rollup"
864 | },
865 | "engines": {
866 | "node": ">=18.0.0",
867 | "npm": ">=8.0.0"
868 | },
869 | "optionalDependencies": {
870 | "@rollup/rollup-android-arm-eabi": "4.14.0",
871 | "@rollup/rollup-android-arm64": "4.14.0",
872 | "@rollup/rollup-darwin-arm64": "4.14.0",
873 | "@rollup/rollup-darwin-x64": "4.14.0",
874 | "@rollup/rollup-linux-arm-gnueabihf": "4.14.0",
875 | "@rollup/rollup-linux-arm64-gnu": "4.14.0",
876 | "@rollup/rollup-linux-arm64-musl": "4.14.0",
877 | "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0",
878 | "@rollup/rollup-linux-riscv64-gnu": "4.14.0",
879 | "@rollup/rollup-linux-s390x-gnu": "4.14.0",
880 | "@rollup/rollup-linux-x64-gnu": "4.14.0",
881 | "@rollup/rollup-linux-x64-musl": "4.14.0",
882 | "@rollup/rollup-win32-arm64-msvc": "4.14.0",
883 | "@rollup/rollup-win32-ia32-msvc": "4.14.0",
884 | "@rollup/rollup-win32-x64-msvc": "4.14.0",
885 | "fsevents": "~2.3.2"
886 | }
887 | },
888 | "node_modules/sass": {
889 | "version": "1.74.1",
890 | "resolved": "https://registry.npmjs.org/sass/-/sass-1.74.1.tgz",
891 | "integrity": "sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==",
892 | "dev": true,
893 | "dependencies": {
894 | "chokidar": ">=3.0.0 <4.0.0",
895 | "immutable": "^4.0.0",
896 | "source-map-js": ">=0.6.2 <2.0.0"
897 | },
898 | "bin": {
899 | "sass": "sass.js"
900 | },
901 | "engines": {
902 | "node": ">=14.0.0"
903 | }
904 | },
905 | "node_modules/source-map-js": {
906 | "version": "1.2.0",
907 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
908 | "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
909 | "dev": true,
910 | "engines": {
911 | "node": ">=0.10.0"
912 | }
913 | },
914 | "node_modules/to-regex-range": {
915 | "version": "5.0.1",
916 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
917 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
918 | "dev": true,
919 | "dependencies": {
920 | "is-number": "^7.0.0"
921 | },
922 | "engines": {
923 | "node": ">=8.0"
924 | }
925 | },
926 | "node_modules/vite": {
927 | "version": "5.2.8",
928 | "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
929 | "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
930 | "dev": true,
931 | "dependencies": {
932 | "esbuild": "^0.20.1",
933 | "postcss": "^8.4.38",
934 | "rollup": "^4.13.0"
935 | },
936 | "bin": {
937 | "vite": "bin/vite.js"
938 | },
939 | "engines": {
940 | "node": "^18.0.0 || >=20.0.0"
941 | },
942 | "funding": {
943 | "url": "https://github.com/vitejs/vite?sponsor=1"
944 | },
945 | "optionalDependencies": {
946 | "fsevents": "~2.3.3"
947 | },
948 | "peerDependencies": {
949 | "@types/node": "^18.0.0 || >=20.0.0",
950 | "less": "*",
951 | "lightningcss": "^1.21.0",
952 | "sass": "*",
953 | "stylus": "*",
954 | "sugarss": "*",
955 | "terser": "^5.4.0"
956 | },
957 | "peerDependenciesMeta": {
958 | "@types/node": {
959 | "optional": true
960 | },
961 | "less": {
962 | "optional": true
963 | },
964 | "lightningcss": {
965 | "optional": true
966 | },
967 | "sass": {
968 | "optional": true
969 | },
970 | "stylus": {
971 | "optional": true
972 | },
973 | "sugarss": {
974 | "optional": true
975 | },
976 | "terser": {
977 | "optional": true
978 | }
979 | }
980 | }
981 | }
982 | }
983 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sorting-visualizer",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "devDependencies": {
12 | "sass": "^1.74.1",
13 | "vite": "^5.2.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TylerMommsen/sorting-visualizer/0bc69165e2cb6a28c2b095733ced464393b19f0f/public/favicon.png
--------------------------------------------------------------------------------
/public/swapsound.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TylerMommsen/sorting-visualizer/0bc69165e2cb6a28c2b095733ced464393b19f0f/public/swapsound.mp3
--------------------------------------------------------------------------------
/src/_reset.scss:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | margin: 0;
4 | padding: 0;
5 | color: $primary;
6 | font-family: sans-serif;
7 | }
8 |
9 | button {
10 | border: none;
11 | background-color: transparent;
12 | }
13 |
--------------------------------------------------------------------------------
/src/algorithms/BubbleSort.js:
--------------------------------------------------------------------------------
1 | import { speed } from "../main";
2 | import DisplayBars from "../utils/DisplayBars";
3 |
4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5 |
6 | export default async function BubbleSort(bars) {
7 | let swapped = false;
8 | do {
9 | swapped = false;
10 | for (let i = 0; i < bars.length; i++) {
11 | if (bars[i - 1] > bars[i]) {
12 | swapped = true;
13 | [bars[i - 1], bars[i]] = [bars[i], bars[i - 1]];
14 | DisplayBars(bars, [i - 1, i]);
15 | await delay(speed);
16 | }
17 | }
18 | } while (swapped);
19 |
20 | DisplayBars(bars);
21 |
22 | return;
23 | }
24 |
--------------------------------------------------------------------------------
/src/algorithms/BucketSort.js:
--------------------------------------------------------------------------------
1 | import DisplayBars from "../utils/DisplayBars";
2 | import InsertionSort from "./InsertionSort";
3 |
4 | export default async function BucketSort(arr) {
5 | let n = arr.length;
6 | let buckets = Array.from({ length: n }, () => []);
7 |
8 | // put array elements in different buckets
9 | for (let i = 0; i < n; i++) {
10 | let bi = Math.floor(n * arr[i]);
11 | bi = Math.min(bi, n - 1); // Ensure bi is within bounds
12 | buckets[bi].push(arr[i]);
13 | }
14 |
15 | // sort individual buckets using insertion sort
16 | for (let i = 0; i < n; i++) {
17 | await InsertionSort(buckets[i], 0, buckets[i].length - 1);
18 | }
19 |
20 | // concatenate all buckets into arr[]
21 | let index = 0;
22 | for (let i = 0; i < n; i++) {
23 | for (let j = 0; j < buckets[i].length; j++) {
24 | arr[index++] = buckets[i][j];
25 | }
26 | }
27 |
28 | DisplayBars(arr);
29 |
30 | return;
31 | }
32 |
--------------------------------------------------------------------------------
/src/algorithms/HeapSort.js:
--------------------------------------------------------------------------------
1 | import { speed } from "../main";
2 | import DisplayBars from "../utils/DisplayBars";
3 |
4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5 |
6 | async function Heapify(arr, n, i) {
7 | let largest = i;
8 | let l = 2 * i + 1;
9 | let r = 2 * i + 2;
10 |
11 | if (l < n && arr[l] > arr[largest]) largest = l;
12 |
13 | if (r < n && arr[r] > arr[largest]) largest = r;
14 |
15 | if (largest != i) {
16 | let swap = arr[i];
17 | arr[i] = arr[largest];
18 | arr[largest] = swap;
19 | DisplayBars(arr, [i, largest]);
20 | await delay(speed);
21 |
22 | await Heapify(arr, n, largest);
23 | }
24 | }
25 |
26 | export default async function HeapSort(arr) {
27 | let n = arr.length;
28 |
29 | for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
30 | await Heapify(arr, n, i);
31 | }
32 |
33 | for (let i = n - 1; i > 0; i--) {
34 | let temp = arr[0];
35 | arr[0] = arr[i];
36 | arr[i] = temp;
37 | DisplayBars(arr, [0, i]);
38 | await delay(speed);
39 |
40 | await Heapify(arr, i, 0);
41 | }
42 |
43 | DisplayBars(arr);
44 |
45 | return;
46 | }
47 |
--------------------------------------------------------------------------------
/src/algorithms/InsertionSort.js:
--------------------------------------------------------------------------------
1 | import { speed } from "../main";
2 | import DisplayBars from "../utils/DisplayBars";
3 |
4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5 |
6 | export default async function InsertionSort(arr, left, right) {
7 | let i, key, j;
8 |
9 | for (i = left + 1; i <= right; i++) {
10 | key = arr[i];
11 | j = i - 1;
12 |
13 | while (j >= 0 && arr[j] > key) {
14 | arr[j + 1] = arr[j];
15 | DisplayBars(arr, [j + 1]);
16 | await delay(speed);
17 |
18 | j = j - 1;
19 | }
20 |
21 | arr[j + 1] = key;
22 | }
23 |
24 | DisplayBars(arr);
25 |
26 | return;
27 | }
28 |
--------------------------------------------------------------------------------
/src/algorithms/MergeSort.js:
--------------------------------------------------------------------------------
1 | import { speed } from "../main";
2 | import DisplayBars from "../utils/DisplayBars";
3 |
4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5 |
6 | export async function Merge(arr, left, middle, right) {
7 | const n1 = middle - left + 1;
8 | const n2 = right - middle;
9 |
10 | // create temp arrays
11 | const L = new Array(n1);
12 | const R = new Array(n2);
13 |
14 | for (let i = 0; i < n1; i++) {
15 | L[i] = arr[left + i];
16 | }
17 |
18 | for (let j = 0; j < n2; j++) {
19 | R[j] = arr[middle + 1 + j];
20 | }
21 |
22 | // merge the temp arrays back into arr[left..right]
23 | let i = 0; // initial index of the first subarray
24 | let j = 0; // initial index of the second subarray
25 | let k = left; // initial index of the merged subarray
26 |
27 | while (i < n1 && j < n2) {
28 | if (L[i] <= R[j]) {
29 | arr[k] = L[i];
30 | DisplayBars(arr, [k]);
31 | i++;
32 | } else {
33 | arr[k] = R[j];
34 | DisplayBars(arr, [k]);
35 | j++;
36 | }
37 | await delay(speed);
38 | k++;
39 | }
40 |
41 | // copy the remaining elements of L[] if there are any
42 | while (i < n1) {
43 | arr[k] = L[i];
44 | DisplayBars(arr, [k]);
45 | await delay(speed);
46 | i++;
47 | k++;
48 | }
49 |
50 | // copy the remaining elements of R[] if there are any
51 | while (j < n2) {
52 | arr[k] = R[j];
53 | DisplayBars(arr, [k]);
54 | await delay(speed);
55 | j++;
56 | k++;
57 | }
58 | }
59 |
60 | export default async function MergeSort(arr, left, right) {
61 | async function Algorithm(arr, left, right) {
62 | if (left >= right) {
63 | return; // returns recursively
64 | }
65 |
66 | const middle = left + Math.floor((right - left) / 2);
67 | await Algorithm(arr, left, middle);
68 | await Algorithm(arr, middle + 1, right);
69 | await Merge(arr, left, middle, right);
70 | }
71 |
72 | await Algorithm(arr, left, right - 1);
73 |
74 | DisplayBars(arr);
75 |
76 | return;
77 | }
78 |
--------------------------------------------------------------------------------
/src/algorithms/Quicksort.js:
--------------------------------------------------------------------------------
1 | import { speed } from "../main";
2 | import DisplayBars from "../utils/DisplayBars";
3 |
4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5 |
6 | export default async function Quicksort(arr, low, high) {
7 | // function that handles partitioning the array and returning the partition index
8 | async function Partition(arr, low, high) {
9 | let pivot = arr[high];
10 | let i = low - 1;
11 |
12 | for (let j = low; j <= high - 1; j++) {
13 | if (arr[j] < pivot) {
14 | i++;
15 | [arr[i], arr[j]] = [arr[j], arr[i]]; // swap elements
16 | DisplayBars(arr, [i, j]);
17 | await delay(speed);
18 | }
19 | }
20 |
21 | [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]]; // swap pivot to its correct position
22 | return i + 1;
23 | }
24 |
25 | // the main quicksort algorithm
26 | async function Algorithm(arr, low, high) {
27 | if (low < high) {
28 | // pi is the partitioning index, arr[pi] is now at the right place
29 | let pi = await Partition(arr, low, high);
30 |
31 | // Seperately sort elements before parition and after partition
32 | await Algorithm(arr, low, pi - 1);
33 | await Algorithm(arr, pi + 1, high);
34 | }
35 | }
36 |
37 | await Algorithm(arr, low, high);
38 |
39 | DisplayBars(arr);
40 |
41 | return;
42 | }
43 |
--------------------------------------------------------------------------------
/src/algorithms/RadixSort.js:
--------------------------------------------------------------------------------
1 | import { speed } from "../main";
2 | import DisplayBars from "../utils/DisplayBars";
3 |
4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5 |
6 | function GetPosition(num, place) {
7 | return Math.floor(Math.abs(num) / Math.pow(10, place)) % 10;
8 | }
9 |
10 | function GetMax(arr) {
11 | let max = 0;
12 | for (let num of arr) {
13 | if (max < Math.floor(num).toString().length) {
14 | max = Math.floor(num).toString().length;
15 | }
16 | }
17 | return max;
18 | }
19 |
20 | export default async function RadixSort(arr) {
21 | const max = GetMax(arr); // Get the maximum number of digits
22 |
23 | for (let i = 0; i < max; i++) {
24 | let buckets = Array.from({ length: 10 }, () => []);
25 |
26 | // Distribute the array elements into buckets
27 | for (let j = 0; j < arr.length; j++) {
28 | buckets[GetPosition(arr[j], i)].push(arr[j]);
29 |
30 | DisplayBars(arr, [j]);
31 | await delay(speed);
32 | }
33 |
34 | // Flatten the buckets back into the array
35 | arr = [].concat(...buckets);
36 | }
37 |
38 | DisplayBars(arr);
39 |
40 | return arr;
41 | }
42 |
--------------------------------------------------------------------------------
/src/algorithms/SelectionSort.js:
--------------------------------------------------------------------------------
1 | import { speed } from "../main";
2 | import DisplayBars from "../utils/DisplayBars";
3 |
4 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5 |
6 | export default async function SelectionSort(arr, n) {
7 | async function Swap(arr, xp, yp) {
8 | let temp = arr[xp];
9 | arr[xp] = arr[yp];
10 | arr[yp] = temp;
11 | DisplayBars(arr, [xp, yp]);
12 | await delay(speed);
13 | }
14 |
15 | async function Algorithm(arr, n) {
16 | let i, j, minIdx;
17 |
18 | for (i = 0; i < n - 1; i++) {
19 | minIdx = i;
20 | for (j = i + 1; j < n; j++) {
21 | if (arr[j] < arr[minIdx]) {
22 | minIdx = j;
23 | }
24 | }
25 |
26 | await Swap(arr, minIdx, i);
27 | }
28 | }
29 |
30 | await Algorithm(arr, n);
31 |
32 | DisplayBars(arr);
33 |
34 | return;
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/Bar.js:
--------------------------------------------------------------------------------
1 | export default function Bar() {
2 | const bar = document.createElement("div");
3 | bar.classList.add("bar");
4 | return bar;
5 | }
6 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import "./style.scss";
2 | import BubbleSort from "./algorithms/BubbleSort";
3 | import Shuffle from "./utils/Shuffle";
4 | import DisplayBars from "./utils/DisplayBars";
5 | import Quicksort from "./algorithms/Quicksort";
6 | import InsertionSort from "./algorithms/InsertionSort";
7 | import MergeSort from "./algorithms/MergeSort";
8 | import SelectionSort from "./algorithms/SelectionSort";
9 | import RadixSort from "./algorithms/RadixSort";
10 | import BucketSort from "./algorithms/BucketSort";
11 | import HeapSort from "./algorithms/HeapSort";
12 | import IsSorted from "./utils/IsSorted";
13 |
14 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
15 |
16 | let barsLength = 50;
17 | let bars = [];
18 | let currentlySelectedAlgorithm = "Quicksort";
19 | export let speed = 50; // the speed the algorithms execute in milliseconds
20 | let algorithmRunning = false;
21 | export let soundOn = true;
22 |
23 | // create the bars initially or whenever the user changes the bars length size
24 | function InitBars() {
25 | for (let i = 1; i <= barsLength; i++) {
26 | bars.push(i);
27 | }
28 | }
29 |
30 | // display the final sweep of the full array for a nice visual touch
31 | async function VisualizeFinal() {
32 | for (let i = 0; i < barsLength; i++) {
33 | if (i - 1 >= 0 && i + 1 < barsLength) {
34 | DisplayBars(bars, [i - 1, i, i + 1]);
35 | } else if (i - 1 >= 0) {
36 | DisplayBars(bars, [i - 1, i]);
37 | } else {
38 | DisplayBars(bars, [i, i + 1]);
39 | }
40 | await delay(barsLength < 100 ? 30 : speed);
41 | }
42 |
43 | DisplayBars(bars);
44 | }
45 |
46 | const selectAlgorithmBtn = document.getElementById("select-algorithm");
47 | const algorithmOptions = document.querySelector(".algorithm-options");
48 | const sizeSlider = document.getElementById("size-slider");
49 | const visualizeBtn = document.getElementById("visualize-btn");
50 | const shuffleBtn = document.getElementById("shuffle-btn");
51 | const speedSlider = document.getElementById("speed-slider");
52 | const speakerBtnOn = document.querySelector(".speaker-btn-on");
53 | const speakerBtnOff = document.querySelector(".speaker-btn-off");
54 |
55 | // hide or open dropdown list for algorithms
56 | selectAlgorithmBtn.addEventListener("click", function (event) {
57 | algorithmOptions.classList.toggle("show");
58 | event.stopPropagation();
59 | });
60 |
61 | // add listener to each algorithm option in dropdown list
62 | algorithmOptions.querySelectorAll("div").forEach(function (option) {
63 | option.addEventListener("click", function () {
64 | selectAlgorithmBtn.querySelector("p").textContent = option.textContent;
65 | currentlySelectedAlgorithm = option.textContent;
66 | visualizeBtn.textContent = `Visualize ${option.textContent}`;
67 | algorithmOptions.classList.remove("show");
68 | });
69 | });
70 |
71 | // close dropdown menu when user clicks anywhere else on screen
72 | document.addEventListener("click", function (event) {
73 | let targetElement = event.target;
74 | let clickInsideDropdown =
75 | algorithmOptions.contains(targetElement) || selectAlgorithmBtn.contains(targetElement);
76 |
77 | if (!clickInsideDropdown && algorithmOptions.classList.contains("show")) {
78 | algorithmOptions.classList.remove("show");
79 | }
80 | });
81 |
82 | speakerBtnOn.addEventListener("click", function () {
83 | if (soundOn) {
84 | soundOn = false;
85 | } else {
86 | soundOn = true;
87 | }
88 |
89 | speakerBtnOn.classList.toggle("hidden");
90 | speakerBtnOff.classList.toggle("hidden");
91 | });
92 |
93 | speakerBtnOff.addEventListener("click", function () {
94 | if (!soundOn) {
95 | soundOn = true;
96 | } else {
97 | soundOn = false;
98 | }
99 |
100 | speakerBtnOff.classList.toggle("hidden");
101 | speakerBtnOn.classList.toggle("hidden");
102 | });
103 |
104 | // disable size slider when algorithm is running
105 | function toggleSliderDisabledState(isDisabled) {
106 | const slider = document.getElementById("size-slider");
107 | slider.disabled = isDisabled;
108 | }
109 |
110 | // adjust the amount of bars
111 | sizeSlider.addEventListener("input", function () {
112 | if (!algorithmRunning) {
113 | const sizeValue = parseInt(this.value);
114 | document.getElementById("size-value").textContent = sizeValue;
115 | barsLength = sizeValue;
116 | bars = [];
117 | InitBars();
118 | DisplayBars(bars);
119 | }
120 | });
121 |
122 | // adjust the speed of the sorting algorithm
123 | speedSlider.addEventListener("input", function () {
124 | const speedValue = parseInt(this.value);
125 | document.getElementById("speed-value").textContent = speedValue + "ms";
126 | speed = speedValue;
127 | });
128 |
129 | // start the sorting algorithm
130 | visualizeBtn.addEventListener("click", async function () {
131 | if (algorithmRunning) return;
132 |
133 | algorithmRunning = true;
134 | toggleSliderDisabledState(true); // disable size adjustment slider
135 | let isSorted = await IsSorted(bars); // check if the array is already sorted
136 | if (!isSorted) {
137 | switch (currentlySelectedAlgorithm) {
138 | case "Quicksort":
139 | await Quicksort(bars, 0, barsLength - 1);
140 | break;
141 | case "Bubble Sort":
142 | await BubbleSort(bars);
143 | break;
144 | case "Insertion Sort":
145 | await InsertionSort(bars, 0, barsLength - 1);
146 | break;
147 | case "Merge Sort":
148 | await MergeSort(bars, 0, barsLength);
149 | break;
150 | case "Selection Sort":
151 | await SelectionSort(bars, barsLength);
152 | break;
153 | case "Radix Sort":
154 | let newBars = await RadixSort(bars);
155 | bars = [];
156 | bars = [...newBars];
157 | break;
158 | case "Bucket Sort":
159 | await BucketSort(bars);
160 | break;
161 | case "Heap Sort":
162 | await HeapSort(bars);
163 | break;
164 | default:
165 | return;
166 | }
167 | }
168 |
169 | await VisualizeFinal();
170 | algorithmRunning = false;
171 | toggleSliderDisabledState(false);
172 | });
173 |
174 | // shuffle the bars/array
175 | shuffleBtn.addEventListener("click", function () {
176 | if (algorithmRunning) return;
177 |
178 | Shuffle(bars);
179 | DisplayBars(bars);
180 | });
181 |
182 | InitBars();
183 | DisplayBars(bars);
184 |
--------------------------------------------------------------------------------
/src/style.scss:
--------------------------------------------------------------------------------
1 | $background: #161616;
2 | $primary: #f4f4f4;
3 | $secondary: #000;
4 | $tertiary: #199fff;
5 |
6 | @import "reset";
7 |
8 | button {
9 | padding: 0.4rem 0.6rem 0.4rem 0.6rem;
10 | background-color: $tertiary;
11 | border-radius: 0.4rem;
12 | cursor: pointer;
13 | font-weight: bold;
14 | transition: all 0.2s;
15 | font-size: 1rem;
16 |
17 | @media only screen and (min-width: 992px) {
18 | padding: 0.6rem 1rem 0.6rem 1rem;
19 | }
20 |
21 | &:hover {
22 | background-color: rgba($color: $tertiary, $alpha: 0.9);
23 | }
24 |
25 | &:active {
26 | background-color: rgba($color: $tertiary, $alpha: 0.8);
27 | }
28 | }
29 |
30 | #app {
31 | position: relative;
32 | display: flex;
33 | flex-direction: column;
34 | align-items: center;
35 | height: 100vh;
36 | background-color: $background;
37 | width: 100vw;
38 | }
39 |
40 | // add grid as background
41 | #background {
42 | position: absolute;
43 | top: 0;
44 | left: 0;
45 | width: 100%;
46 | height: 100%;
47 | background-image: linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px),
48 | linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
49 | background-size: 5vw 5vw;
50 | z-index: 0;
51 |
52 | // adds vignette to page
53 | &::before {
54 | content: "";
55 | position: fixed;
56 | top: 0;
57 | left: 0;
58 | width: 100%;
59 | height: 100%;
60 | background-image: radial-gradient(ellipse at center, transparent 80%, rgba(0, 0, 0, 0.2)),
61 | radial-gradient(ellipse at center, transparent 90%, rgba(10, 10, 10, 0.4));
62 | pointer-events: none;
63 | }
64 |
65 | @media only screen and (min-width: 768px) {
66 | background-size: 3vw 3vw;
67 | }
68 |
69 | @media only screen and (min-width: 1200px) {
70 | background-size: 2vw 2vw;
71 | }
72 | }
73 |
74 | header {
75 | width: 100%;
76 | position: relative;
77 | display: flex;
78 | justify-content: center;
79 | align-items: center;
80 | padding: 1rem;
81 | z-index: 1;
82 | z-index: 10;
83 |
84 | &::before {
85 | content: "";
86 | position: absolute;
87 | background-color: $background;
88 | top: 0;
89 | left: 0;
90 | width: 100%;
91 | height: 100%;
92 | z-index: -1;
93 | box-shadow: 0 10px 20px rgba($color: $background, $alpha: 0.8);
94 | }
95 | }
96 |
97 | main {
98 | z-index: 1;
99 | flex: 1;
100 | display: flex;
101 | flex-direction: column;
102 | align-items: center;
103 | gap: 5rem;
104 | width: 100%;
105 | max-width: 1280px;
106 | padding: 1rem 1rem 0 1rem;
107 |
108 | .btn-selections {
109 | margin-top: 1rem;
110 | display: flex;
111 | width: 100%;
112 | gap: 1rem;
113 | justify-content: space-between;
114 |
115 | @media only screen and (min-width: 992px) {
116 | align-items: center;
117 | }
118 |
119 | .algorithms {
120 | display: flex;
121 | flex-direction: column;
122 | gap: 0.4rem;
123 |
124 | @media only screen and (min-width: 992px) {
125 | flex-direction: row;
126 | gap: 1rem;
127 | }
128 |
129 | #select-algorithm {
130 | display: flex;
131 | align-items: center;
132 | justify-content: space-between;
133 | gap: 1rem;
134 | border-radius: 0.4rem;
135 | cursor: pointer;
136 | font-weight: bold;
137 | background-color: $primary;
138 | font-size: 1rem;
139 | padding: 0.4rem 0.6rem 0.4rem 0.6rem;
140 |
141 | @media only screen and (min-width: 992px) {
142 | padding: 0.6rem 1rem 0.6rem 1rem;
143 | }
144 |
145 | &:hover {
146 | background-color: rgba($color: $primary, $alpha: 0.9);
147 | }
148 |
149 | &:active {
150 | background-color: rgba($color: $primary, $alpha: 0.8);
151 | }
152 |
153 | p {
154 | color: $secondary;
155 | }
156 |
157 | // down arrow
158 | span {
159 | display: flex;
160 | align-items: center;
161 |
162 | svg {
163 | width: 1rem;
164 | }
165 | }
166 | }
167 |
168 | .algorithm-options {
169 | color: $secondary;
170 | display: flex;
171 | flex-direction: column;
172 | position: absolute;
173 | top: 3rem;
174 | left: 0;
175 | background-color: $primary;
176 | border-radius: 0.4rem;
177 | white-space: nowrap;
178 | overflow: hidden;
179 | transition: all 0.2s ease;
180 | visibility: hidden;
181 | transform: translateY(-10px);
182 | opacity: 0;
183 |
184 | .option {
185 | color: $secondary;
186 | cursor: pointer;
187 | font-weight: bold;
188 | padding: 0.4rem 1rem 0.4rem 1rem;
189 | transition: all 0.1s;
190 | overflow: hidden;
191 |
192 | &:hover {
193 | background-color: $tertiary;
194 | color: $primary;
195 | }
196 | }
197 | }
198 |
199 | .algorithm-options.show {
200 | visibility: visible;
201 | opacity: 1;
202 | transform: translateY(0);
203 | }
204 |
205 | .speaker-btn {
206 | cursor: pointer;
207 | width: 2rem;
208 | }
209 |
210 | .speaker-btn.hidden {
211 | display: none;
212 | }
213 | }
214 |
215 | .customization-btns {
216 | display: flex;
217 | flex-direction: column;
218 | justify-content: flex-start;
219 | width: 50%;
220 | gap: 1rem;
221 |
222 | @media only screen and (min-width: 992px) {
223 | flex-direction: row;
224 | justify-content: flex-end;
225 | }
226 |
227 | // select the slider containers
228 | div {
229 | @media only screen and (min-width: 992px) {
230 | flex: 1;
231 | }
232 |
233 | // select the slider inputs
234 | input {
235 | width: 100%;
236 | margin-top: 0.3rem;
237 | }
238 | }
239 | }
240 | }
241 |
242 | #bar-container {
243 | margin-top: auto;
244 | display: flex;
245 | justify-content: center;
246 | align-items: flex-end;
247 | width: 100%;
248 | height: 80%;
249 |
250 | .bar {
251 | background: linear-gradient(0deg, rgba(22, 22, 22, 1) 5%, rgba(210, 210, 210, 1) 100%);
252 | }
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/src/utils/DisplayBars.js:
--------------------------------------------------------------------------------
1 | import { soundOn } from "../main";
2 | import { playSound } from "./SoundManager";
3 | import Bar from "../components/Bar";
4 |
5 | export default async function DisplayBars(bars, currentlySwapped) {
6 | // give each bar a relative value between 0 and 100 to give a nice staircase look when displaying finished sort
7 | let normalizedBars = bars.map((bar) => {
8 | return (bar * 100) / bars.length;
9 | });
10 |
11 | let barContainer = document.getElementById("bar-container");
12 | barContainer.innerHTML = "";
13 | for (let i = 0; i < normalizedBars.length; i++) {
14 | let currBar = Bar();
15 | currBar.style.height = normalizedBars[i] + "%";
16 | currBar.style.width = 100 / normalizedBars.length + "%";
17 |
18 | if (currentlySwapped && currentlySwapped.includes(i)) {
19 | currBar.style.background = "linear-gradient(to top right, #ff6a00, #ff9143)";
20 | const pitchRate = bars[i] / Math.max(...bars) + 0.5;
21 | // Play the sound with adjusted pitch
22 | if (soundOn) {
23 | playSound(pitchRate);
24 | }
25 | }
26 |
27 | barContainer.appendChild(currBar);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/utils/IsSorted.js:
--------------------------------------------------------------------------------
1 | export default async function IsSorted(arr) {
2 | for (let i = 0; i < arr.length - 1; i++) {
3 | if (arr[i] > arr[i + 1]) {
4 | return false; // Early return if any element is greater than its successor
5 | }
6 | }
7 | return true; // The array is sorted
8 | }
9 |
--------------------------------------------------------------------------------
/src/utils/Shuffle.js:
--------------------------------------------------------------------------------
1 | export default function Shuffle(bars) {
2 | for (let i = bars.length - 1; i > 0; i--) {
3 | const j = Math.floor(Math.random() * (i + 1));
4 |
5 | [bars[i], bars[j]] = [bars[j], bars[i]];
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils/SoundManager.js:
--------------------------------------------------------------------------------
1 | let sound = null;
2 |
3 | // Function to load sound
4 | async function loadSound(url) {
5 | const response = await fetch(url);
6 | const arrayBuffer = await response.arrayBuffer();
7 | const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
8 | return audioBuffer;
9 | }
10 |
11 | // Initialize the Audio Context
12 | const audioContext = new (window.AudioContext || window.webkitAudioContext)();
13 | const gainNode = audioContext.createGain();
14 | gainNode.connect(audioContext.destination);
15 |
16 | let playingSounds = 0;
17 |
18 | export async function playSound(pitchRate) {
19 | const sound = await getSound();
20 |
21 | playingSounds++;
22 | updateGain(playingSounds);
23 |
24 | const source = audioContext.createBufferSource();
25 | source.buffer = sound;
26 | source.playbackRate.value = pitchRate;
27 | source.connect(gainNode);
28 | source.onended = () => {
29 | playingSounds--;
30 | updateGain(playingSounds);
31 | };
32 | source.start();
33 | }
34 |
35 | function updateGain(numberOfSounds) {
36 | const baseVolume = 0.5;
37 | // Check if numberOfSounds is 0 to avoid division by 0
38 | if (numberOfSounds <= 1) {
39 | gainNode.gain.value = baseVolume; // Set to default/full volume when no sounds are playing
40 | } else {
41 | // Decrease volume as more sounds play
42 | gainNode.gain.value = baseVolume / Math.sqrt(numberOfSounds);
43 | }
44 | }
45 |
46 | export async function getSound() {
47 | if (!sound) {
48 | sound = await loadSound("/swapsound.mp3");
49 | }
50 | return sound;
51 | }
52 |
--------------------------------------------------------------------------------