brought both code in 1 repo, working on merging them!
This commit is contained in:
parent
d1c8ae269b
commit
9f927cf2e9
82 changed files with 3220 additions and 20 deletions
12
package.json
12
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "kahootclone",
|
||||
"private": true,
|
||||
"name": "class-room-stuff",
|
||||
"private": false,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
@ -27,6 +27,12 @@
|
|||
"vite": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^2.49.4"
|
||||
"@supabase/supabase-js": "^2.49.4",
|
||||
"chart.js": "^4.4.9",
|
||||
"drag-drop-touch": "^1.3.1",
|
||||
"kokoro-js": "^1.2.1",
|
||||
"random-words": "^2.0.1",
|
||||
"svelte-spa-router": "^4.0.1",
|
||||
"word-exists": "^1.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,34 @@
|
|||
<script>
|
||||
let { children } = $props();
|
||||
|
||||
import { confettiAnimation } from "./Confetti.js";
|
||||
let ShowAlertDiv = $state(false);
|
||||
let ShowAlertText = $state("this Code is bad");
|
||||
let ShowAlertType = $state("Error");
|
||||
|
||||
export function ShowAlert(text, type) {
|
||||
ShowAlertDiv = true;
|
||||
ShowAlertType = type;
|
||||
|
||||
ShowAlertText = text;
|
||||
setTimeout(() => {
|
||||
ShowAlertDiv = false;
|
||||
}, 1500);
|
||||
|
||||
if (type == "success") {
|
||||
confettiAnimation();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div class="h-full text-white">{@render children()}</div>
|
||||
</div>
|
||||
{#if ShowAlertDiv == true}
|
||||
<div id="alert">
|
||||
<h1 class={ShowAlertType}>{ShowAlertText}</h1>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
@import "https://www.nerdfonts.com/assets/css/webfont.css";
|
||||
|
@ -14,4 +40,33 @@
|
|||
font-family: "Comfortaa", sans-serif;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
font-family: "Sour Gummy", sans-serif;
|
||||
}
|
||||
|
||||
#alert {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
color: white;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 10px 20px;
|
||||
border-radius: 20px;
|
||||
font-family: "Sour Gummy", sans-serif;
|
||||
}
|
||||
.error {
|
||||
background-color: #830000;
|
||||
}
|
||||
.warning {
|
||||
background-color: #975b00;
|
||||
}
|
||||
.success {
|
||||
background-color: #006b00;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,18 +1,246 @@
|
|||
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg">
|
||||
<h1 class="m-[0] text-6xl">DaKahootClone</h1>
|
||||
<p class="m-[0] mb-2 text-lg text-gray-400">The best ever kahoot clone.</p>
|
||||
<a href="./join">
|
||||
<button
|
||||
class="cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
>Join a game</button
|
||||
></a
|
||||
<div id="root">
|
||||
<img
|
||||
src="https://hc-cdn.hel1.your-objectstorage.com/s/v3/77e7a04f27807b4e0c16bcda09ea222f9e091616_group_18.svg"
|
||||
id="logo"
|
||||
alt="ClassRoomStuff Logo"
|
||||
/>
|
||||
<h2>A collection of awesome tools, games, and more — made to be used in any classroom!</h2>
|
||||
<h3>MAIN</h3>
|
||||
|
||||
<div id="items2">
|
||||
<a href="/KahhotClone">
|
||||
<button class="button2">
|
||||
<span class="front2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="70px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="70px"
|
||||
fill="#e3e3e3"
|
||||
><path
|
||||
d="M560-360q17 0 29.5-12.5T602-402q0-17-12.5-29.5T560-444q-17 0-29.5 12.5T518-402q0 17 12.5 29.5T560-360Zm-30-128h60q0-29 6-42.5t28-35.5q30-30 40-48.5t10-43.5q0-45-31.5-73.5T560-760q-41 0-71.5 23T446-676l54 22q9-25 24.5-37.5T560-704q24 0 39 13.5t15 36.5q0 14-8 26.5T578-596q-33 29-40.5 45.5T530-488ZM320-240q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"
|
||||
/></svg
|
||||
>
|
||||
<a href="./create">
|
||||
<button
|
||||
class="cursor-pointer rounded-full bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
>Create and Host a game</button
|
||||
DaKahootClone
|
||||
<p>The best ever kahoot clone.</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
</div>
|
||||
<h3>OTHERS</h3>
|
||||
<div id="items">
|
||||
<a href="/IdleScreen">
|
||||
<button>
|
||||
<span class="front">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="30px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="30px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="m612-292 56-56-148-148v-184h-80v216l172 172ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-400Zm0 320q133 0 226.5-93.5T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 133 93.5 226.5T480-160Z"
|
||||
/></svg
|
||||
>
|
||||
Timetable
|
||||
<p>Clock included!</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
<a href="#/RandomName">
|
||||
<button>
|
||||
<span class="front">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="30px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="30px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M0-240v-63q0-43 44-70t116-27q13 0 25 .5t23 2.5q-14 21-21 44t-7 48v65H0Zm240 0v-65q0-32 17.5-58.5T307-410q32-20 76.5-30t96.5-10q53 0 97.5 10t76.5 30q32 20 49 46.5t17 58.5v65H240Zm540 0v-65q0-26-6.5-49T754-397q11-2 22.5-2.5t23.5-.5q72 0 116 26.5t44 70.5v63H780Zm-455-80h311q-10-20-55.5-35T480-370q-55 0-100.5 15T325-320ZM160-440q-33 0-56.5-23.5T80-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T160-440Zm640 0q-33 0-56.5-23.5T720-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T800-440Zm-320-40q-50 0-85-35t-35-85q0-51 35-85.5t85-34.5q51 0 85.5 34.5T600-600q0 50-34.5 85T480-480Zm0-80q17 0 28.5-11.5T520-600q0-17-11.5-28.5T480-640q-17 0-28.5 11.5T440-600q0 17 11.5 28.5T480-560Zm1 240Zm-1-280Z"
|
||||
/></svg
|
||||
>
|
||||
Name selector
|
||||
<p>for any class activity!</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
<a href="#/Wordle">
|
||||
<button>
|
||||
<span class="front">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="30px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="30px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M182-200q-51 0-79-35.5T82-322l42-300q9-60 53.5-99T282-760h396q60 0 104.5 39t53.5 99l42 300q7 51-21 86.5T778-200q-21 0-39-7.5T706-230l-90-90H344l-90 90q-15 15-33 22.5t-39 7.5Zm16-86 114-114h336l114 114q2 2 16 6 11 0 17.5-6.5T800-304l-44-308q-4-29-26-48.5T678-680H282q-30 0-52 19.5T204-612l-44 308q-2 11 4.5 17.5T182-280q2 0 16-6Zm482-154q17 0 28.5-11.5T720-480q0-17-11.5-28.5T680-520q-17 0-28.5 11.5T640-480q0 17 11.5 28.5T680-440Zm-80-120q17 0 28.5-11.5T640-600q0-17-11.5-28.5T600-640q-17 0-28.5 11.5T560-600q0 17 11.5 28.5T600-560ZM310-440h60v-70h70v-60h-70v-70h-60v70h-70v60h70v70Zm170-40Z"
|
||||
/></svg
|
||||
>
|
||||
Wordle
|
||||
<p>Challenge your vocabulary!</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
|
||||
<a href="#/announcer">
|
||||
<button>
|
||||
<span class="front">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="30px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="30px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M720-440v-80h160v80H720Zm48 280-128-96 48-64 128 96-48 64Zm-80-480-48-64 128-96 48 64-128 96ZM200-200v-160h-40q-33 0-56.5-23.5T80-440v-80q0-33 23.5-56.5T160-600h160l200-120v480L320-360h-40v160h-80Zm240-182v-196l-98 58H160v80h182l98 58Zm120 36v-268q27 24 43.5 58.5T620-480q0 41-16.5 75.5T560-346ZM300-480Z"
|
||||
/></svg
|
||||
>
|
||||
Announcer
|
||||
<p>to shout from the speakers!</p></span
|
||||
>
|
||||
</button></a
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="BottomBadge">
|
||||
Made by <a href="https://rezhack.xyz" target="_blank">RezHackXYZ</a> for
|
||||
<a href="https://neighborhood.hackclub.com/" target="_blank">Neighborhood</a>!
|
||||
<a href="https://github.com/RezHackXYZ/ClassRoomStuff" target="_blank">Contribute here</a> if you
|
||||
want.
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
font-size: 60px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
color: #797979;
|
||||
font-size: 30px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
#items2 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#items {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #292929;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.front {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 5px 15px;
|
||||
border-radius: 12px;
|
||||
font-size: 1.25rem;
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-7px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.front2 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 3px 30px;
|
||||
border-radius: 12px;
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-7px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 50px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: #aaaaaa;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.front2 > p {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
button:hover .front {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
button:active .front {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
#BottomBadge {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
background-color: #242424;
|
||||
color: #aaaaaa;
|
||||
margin-bottom: 10px;
|
||||
width: fit-content;
|
||||
align-self: center;
|
||||
padding: 5px;
|
||||
font-size: 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #aaaaaa;
|
||||
}
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
color: #aaaaaa;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
width: 500px;
|
||||
margin-bottom: 20px;
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
||||
|
|
316
src/routes/Confetti.js
Normal file
316
src/routes/Confetti.js
Normal file
|
@ -0,0 +1,316 @@
|
|||
export function confettiAnimation() {
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
// Utility functions grouped into a single object
|
||||
const Utils = {
|
||||
// Parse pixel values to numeric values
|
||||
parsePx: (value) => parseFloat(value.replace(/px/, "")),
|
||||
|
||||
// Generate a random number between two values, optionally with a fixed precision
|
||||
getRandomInRange: (min, max, precision = 0) => {
|
||||
const multiplier = Math.pow(10, precision);
|
||||
const randomValue = Math.random() * (max - min) + min;
|
||||
return Math.floor(randomValue * multiplier) / multiplier;
|
||||
},
|
||||
|
||||
// Pick a random item from an array
|
||||
getRandomItem: (array) =>
|
||||
array[Math.floor(Math.random() * array.length)],
|
||||
|
||||
// Scaling factor based on screen width
|
||||
getScaleFactor: () => Math.log(window.innerWidth) / Math.log(1920),
|
||||
|
||||
// Debounce function to limit event firing frequency
|
||||
debounce: (func, delay) => {
|
||||
let timeout;
|
||||
return (...args) => {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func(...args), delay);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// Precomputed constants
|
||||
const DEG_TO_RAD = Math.PI / 180;
|
||||
|
||||
// Centralized configuration for default values
|
||||
const defaultConfettiConfig = {
|
||||
confettiesNumber: 250,
|
||||
confettiRadius: 6,
|
||||
confettiColors: [
|
||||
"#fcf403",
|
||||
"#62fc03",
|
||||
"#f4fc03",
|
||||
"#03e7fc",
|
||||
"#03fca5",
|
||||
"#a503fc",
|
||||
"#fc03ad",
|
||||
"#fc03c2",
|
||||
],
|
||||
emojies: [],
|
||||
svgIcon: null, // Example SVG link
|
||||
};
|
||||
|
||||
// Confetti class representing individual confetti pieces
|
||||
class Confetti {
|
||||
constructor({
|
||||
initialPosition,
|
||||
direction,
|
||||
radius,
|
||||
colors,
|
||||
emojis,
|
||||
svgIcon,
|
||||
}) {
|
||||
const speedFactor =
|
||||
Utils.getRandomInRange(0.9, 1.7, 3) *
|
||||
Utils.getScaleFactor();
|
||||
this.speed = { x: speedFactor, y: speedFactor };
|
||||
this.finalSpeedX = Utils.getRandomInRange(0.2, 0.6, 3);
|
||||
this.rotationSpeed =
|
||||
emojis.length || svgIcon
|
||||
? 0.01
|
||||
: Utils.getRandomInRange(0.03, 0.07, 3) *
|
||||
Utils.getScaleFactor();
|
||||
this.dragCoefficient = Utils.getRandomInRange(
|
||||
0.0005,
|
||||
0.0009,
|
||||
6
|
||||
);
|
||||
this.radius = { x: radius, y: radius };
|
||||
this.initialRadius = radius;
|
||||
this.rotationAngle =
|
||||
direction === "left"
|
||||
? Utils.getRandomInRange(0, 0.2, 3)
|
||||
: Utils.getRandomInRange(-0.2, 0, 3);
|
||||
this.emojiRotationAngle = Utils.getRandomInRange(
|
||||
0,
|
||||
2 * Math.PI
|
||||
);
|
||||
this.radiusYDirection = "down";
|
||||
|
||||
const angle =
|
||||
direction === "left"
|
||||
? Utils.getRandomInRange(82, 15) * DEG_TO_RAD
|
||||
: Utils.getRandomInRange(-15, -82) * DEG_TO_RAD;
|
||||
this.absCos = Math.abs(Math.cos(angle));
|
||||
this.absSin = Math.abs(Math.sin(angle));
|
||||
|
||||
const offset = Utils.getRandomInRange(-150, 0);
|
||||
const position = {
|
||||
x:
|
||||
initialPosition.x +
|
||||
(direction === "left" ? -offset : offset) * this.absCos,
|
||||
y: initialPosition.y - offset * this.absSin,
|
||||
};
|
||||
|
||||
this.position = { ...position };
|
||||
this.initialPosition = { ...position };
|
||||
this.color =
|
||||
emojis.length || svgIcon
|
||||
? null
|
||||
: Utils.getRandomItem(colors);
|
||||
this.emoji = emojis.length ? Utils.getRandomItem(emojis) : null;
|
||||
this.svgIcon = null;
|
||||
|
||||
// Preload SVG if provided
|
||||
if (svgIcon) {
|
||||
this.svgImage = new Image();
|
||||
this.svgImage.src = svgIcon;
|
||||
this.svgImage.onload = () => {
|
||||
this.svgIcon = this.svgImage; // Mark as ready once loaded
|
||||
};
|
||||
}
|
||||
|
||||
this.createdAt = Date.now();
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
draw(context) {
|
||||
const { x, y } = this.position;
|
||||
const { x: radiusX, y: radiusY } = this.radius;
|
||||
const scale = window.devicePixelRatio;
|
||||
|
||||
if (this.svgIcon) {
|
||||
context.save();
|
||||
context.translate(scale * x, scale * y);
|
||||
context.rotate(this.emojiRotationAngle);
|
||||
context.drawImage(
|
||||
this.svgIcon,
|
||||
-radiusX,
|
||||
-radiusY,
|
||||
radiusX * 2,
|
||||
radiusY * 2
|
||||
);
|
||||
context.restore();
|
||||
} else if (this.color) {
|
||||
context.fillStyle = this.color;
|
||||
context.beginPath();
|
||||
context.ellipse(
|
||||
x * scale,
|
||||
y * scale,
|
||||
radiusX * scale,
|
||||
radiusY * scale,
|
||||
this.rotationAngle,
|
||||
0,
|
||||
2 * Math.PI
|
||||
);
|
||||
context.fill();
|
||||
} else if (this.emoji) {
|
||||
context.font = `${radiusX * scale}px serif`;
|
||||
context.save();
|
||||
context.translate(scale * x, scale * y);
|
||||
context.rotate(this.emojiRotationAngle);
|
||||
context.textAlign = "center";
|
||||
context.fillText(this.emoji, 0, radiusY / 2); // Adjust vertical alignment
|
||||
context.restore();
|
||||
}
|
||||
}
|
||||
|
||||
updatePosition(deltaTime, currentTime) {
|
||||
const elapsed = currentTime - this.createdAt;
|
||||
|
||||
if (this.speed.x > this.finalSpeedX) {
|
||||
this.speed.x -= this.dragCoefficient * deltaTime;
|
||||
}
|
||||
|
||||
this.position.x +=
|
||||
this.speed.x *
|
||||
(this.direction === "left" ? -this.absCos : this.absCos) *
|
||||
deltaTime;
|
||||
this.position.y =
|
||||
this.initialPosition.y -
|
||||
this.speed.y * this.absSin * elapsed +
|
||||
(0.00125 * Math.pow(elapsed, 2)) / 2;
|
||||
|
||||
if (!this.emoji && !this.svgIcon) {
|
||||
this.rotationSpeed -= 1e-5 * deltaTime;
|
||||
this.rotationSpeed = Math.max(this.rotationSpeed, 0);
|
||||
|
||||
if (this.radiusYDirection === "down") {
|
||||
this.radius.y -= deltaTime * this.rotationSpeed;
|
||||
if (this.radius.y <= 0) {
|
||||
this.radius.y = 0;
|
||||
this.radiusYDirection = "up";
|
||||
}
|
||||
} else {
|
||||
this.radius.y += deltaTime * this.rotationSpeed;
|
||||
if (this.radius.y >= this.initialRadius) {
|
||||
this.radius.y = this.initialRadius;
|
||||
this.radiusYDirection = "down";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(canvasHeight) {
|
||||
return this.position.y < canvasHeight + 100;
|
||||
}
|
||||
}
|
||||
|
||||
class ConfettiManager {
|
||||
constructor() {
|
||||
this.canvas = document.createElement("canvas");
|
||||
// @ts-ignore
|
||||
this.canvas.style =
|
||||
"position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000; pointer-events: none;";
|
||||
document.body.appendChild(this.canvas);
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.confetti = [];
|
||||
this.lastUpdated = Date.now();
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
Utils.debounce(() => this.resizeCanvas(), 200)
|
||||
);
|
||||
this.resizeCanvas();
|
||||
requestAnimationFrame(() => this.loop());
|
||||
}
|
||||
|
||||
resizeCanvas() {
|
||||
this.canvas.width = window.innerWidth * window.devicePixelRatio;
|
||||
this.canvas.height =
|
||||
window.innerHeight * window.devicePixelRatio;
|
||||
}
|
||||
|
||||
addConfetti(config = {}) {
|
||||
const {
|
||||
confettiesNumber,
|
||||
confettiRadius,
|
||||
confettiColors,
|
||||
emojies,
|
||||
svgIcon,
|
||||
} = {
|
||||
...defaultConfettiConfig,
|
||||
...config,
|
||||
};
|
||||
|
||||
const baseY = (5 * window.innerHeight) / 7;
|
||||
for (let i = 0; i < confettiesNumber / 2; i++) {
|
||||
this.confetti.push(
|
||||
new Confetti({
|
||||
initialPosition: { x: 0, y: baseY },
|
||||
direction: "right",
|
||||
radius: confettiRadius,
|
||||
colors: confettiColors,
|
||||
emojis: emojies,
|
||||
svgIcon,
|
||||
})
|
||||
);
|
||||
this.confetti.push(
|
||||
new Confetti({
|
||||
initialPosition: { x: window.innerWidth, y: baseY },
|
||||
direction: "left",
|
||||
radius: confettiRadius,
|
||||
colors: confettiColors,
|
||||
emojis: emojies,
|
||||
svgIcon,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
resetAndStart(config = {}) {
|
||||
// Clear existing confetti
|
||||
this.confetti = [];
|
||||
// Add new confetti
|
||||
this.addConfetti(config);
|
||||
}
|
||||
|
||||
loop() {
|
||||
const currentTime = Date.now();
|
||||
const deltaTime = currentTime - this.lastUpdated;
|
||||
this.lastUpdated = currentTime;
|
||||
|
||||
this.context.clearRect(
|
||||
0,
|
||||
0,
|
||||
this.canvas.width,
|
||||
this.canvas.height
|
||||
);
|
||||
|
||||
this.confetti = this.confetti.filter((item) => {
|
||||
item.updatePosition(deltaTime, currentTime);
|
||||
item.draw(this.context);
|
||||
return item.isVisible(this.canvas.height);
|
||||
});
|
||||
|
||||
requestAnimationFrame(() => this.loop());
|
||||
}
|
||||
}
|
||||
|
||||
const manager = new ConfettiManager();
|
||||
manager.addConfetti();
|
||||
|
||||
const triggerButton = document.getElementById("show-again");
|
||||
if (triggerButton) {
|
||||
triggerButton.addEventListener("click", () =>
|
||||
manager.addConfetti()
|
||||
);
|
||||
}
|
||||
|
||||
const resetInput = document.getElementById("reset");
|
||||
if (resetInput) {
|
||||
resetInput.addEventListener("input", () => manager.resetAndStart());
|
||||
}
|
||||
})();
|
||||
}
|
180
src/routes/IdleScreen/+page.svelte
Normal file
180
src/routes/IdleScreen/+page.svelte
Normal file
|
@ -0,0 +1,180 @@
|
|||
<script module>
|
||||
import Time from "./time.svelte";
|
||||
import TimeTable from "./timeTable.svelte";
|
||||
import EditTimetableDiv from "./EditTimetable.svelte";
|
||||
|
||||
function EditTimetable() {
|
||||
console.log("Edit timetable");
|
||||
TabOpen.v = true;
|
||||
}
|
||||
|
||||
let TabOpen = $state({ v: false });
|
||||
|
||||
export let ShowSeconds = $state({ v: true });
|
||||
</script>
|
||||
|
||||
<div id="wrap">
|
||||
<div id="nav">
|
||||
<a href="#/" aria-label="Back to main menu"
|
||||
><button aria-label="Back to main menu"
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"
|
||||
/></svg
|
||||
> GO BACK
|
||||
</span></button
|
||||
></a
|
||||
>
|
||||
<h1>Idle Screen</h1>
|
||||
<div>
|
||||
<button
|
||||
aria-label="Back to main menu"
|
||||
onclick={() => {
|
||||
ShowSeconds.v = !ShowSeconds.v;
|
||||
localStorage.setItem("ShowSeconds", String(ShowSeconds.v));
|
||||
}}
|
||||
><span class="front"
|
||||
>{#if ShowSeconds.v}Disable Seconds{:else}Enable Seconds{/if}</span
|
||||
></button
|
||||
><button
|
||||
aria-label="Back to main menu"
|
||||
onclick={() => EditTimetable()}
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v200h-80v-40H200v400h280v80H200Zm0-560h560v-80H200v80Zm0 0v-80 80ZM560-80v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q8 9 12.5 20t4.5 22q0 11-4 22.5T903-300L683-80H560Zm300-263-37-37 37 37ZM620-140h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z"
|
||||
/></svg
|
||||
> Edit timetable
|
||||
</span></button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="root">
|
||||
<TimeTable /><Time />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if TabOpen.v !== false}
|
||||
<div id="UpperLayer">
|
||||
<div id="wrapClose">
|
||||
<EditTimetableDiv />
|
||||
<button
|
||||
class="close"
|
||||
onclick={() => (TabOpen.v = false)}
|
||||
aria-label="close">CLOSE</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
#wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
#nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #292929;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
margin: 10px;
|
||||
padding: 2px 0px;
|
||||
cursor: pointer;
|
||||
outline-offset: 4px;
|
||||
max-width: 400px;
|
||||
}
|
||||
button:hover .front {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
|
||||
button:active .front {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.front {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
border-radius: 12px;
|
||||
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-4px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 25px;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 5px 0px;
|
||||
text-decoration: underline #444;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.5em;
|
||||
text-decoration: none;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
#UpperLayer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backdrop-filter: blur(5px);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#wrapClose {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.close {
|
||||
background-color: #2b2b2b;
|
||||
color: #888;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
348
src/routes/IdleScreen/EditTimetable.svelte
Normal file
348
src/routes/IdleScreen/EditTimetable.svelte
Normal file
|
@ -0,0 +1,348 @@
|
|||
<script>
|
||||
import { newTable } from "./timeTable.svelte";
|
||||
|
||||
let table = JSON.parse(localStorage.getItem("TimeTable"));
|
||||
|
||||
let TabOpen = false;
|
||||
|
||||
let csv = "";
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div id="wrap">
|
||||
<div id="left">
|
||||
<div class="Header Row">
|
||||
<span class="DayOfWeek" style="Opacity: 0;"></span>
|
||||
{#each table.Times as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Monday</span>
|
||||
{#each table.Monday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Tuesday</span>
|
||||
{#each table.Tuesday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Wednesday</span>
|
||||
{#each table.Wednesday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Thursday</span>
|
||||
{#each table.Thursday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
<span class="DayOfWeek">Friday</span>
|
||||
{#each table.Friday as time}
|
||||
<input
|
||||
type="text"
|
||||
onchange={() => {
|
||||
newTable(table);
|
||||
}}
|
||||
bind:value={time}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div id="right">
|
||||
<button
|
||||
onclick={() => {
|
||||
table.Times.push("");
|
||||
table.Monday.push("");
|
||||
table.Tuesday.push("");
|
||||
table.Wednesday.push("");
|
||||
table.Thursday.push("");
|
||||
table.Friday.push("");
|
||||
newTable(table);
|
||||
table = JSON.parse(localStorage.getItem("TimeTable"));
|
||||
}}
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"
|
||||
/></svg
|
||||
>Add new Period
|
||||
</button>
|
||||
<button
|
||||
onclick={() => {
|
||||
if (
|
||||
confirm(
|
||||
"This will remove the last period, it is not reversible! are you sure?"
|
||||
)
|
||||
) {
|
||||
table.Times.pop();
|
||||
table.Monday.pop();
|
||||
table.Tuesday.pop();
|
||||
table.Wednesday.pop();
|
||||
table.Thursday.pop();
|
||||
table.Friday.pop();
|
||||
newTable(table);
|
||||
table = JSON.parse(localStorage.getItem("TimeTable"));
|
||||
}
|
||||
}}
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"
|
||||
/></svg
|
||||
>
|
||||
Delete last Period
|
||||
</button>
|
||||
<button
|
||||
onclick={() => {
|
||||
TabOpen = true;
|
||||
}}
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M230-360h120v-60H250v-120h100v-60H230q-17 0-28.5 11.5T190-560v160q0 17 11.5 28.5T230-360Zm156 0h120q17 0 28.5-11.5T546-400v-60q0-17-11.5-31.5T506-506h-60v-34h100v-60H426q-17 0-28.5 11.5T386-560v60q0 17 11.5 30.5T426-456h60v36H386v60Zm264 0h60l70-240h-60l-40 138-40-138h-60l70 240ZM160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm0-80h640v-480H160v480Zm0 0v-480 480Z"
|
||||
/></svg
|
||||
>
|
||||
Import From CSV
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if TabOpen !== false}
|
||||
<div id="UpperLayer">
|
||||
<div id="wrapClose">
|
||||
<div class="root">
|
||||
<h1>Paste CSV here:</h1>
|
||||
<p></p>
|
||||
<textarea
|
||||
bind:value={csv}
|
||||
id="box"
|
||||
placeholder="Duration 1, Duration 2, Duration 3, Duration 4
|
||||
Subject 01, Subject 02, Subject 03, Subject 04
|
||||
Subject 08, Subject 09, Subject 10, Subject 12
|
||||
Subject 13, Subject 13, Subject 14, Subject 15"
|
||||
></textarea>
|
||||
</div>
|
||||
<div id="options">
|
||||
<button
|
||||
class="close"
|
||||
id="cancel"
|
||||
onclick={() => (TabOpen = false)}
|
||||
aria-label="close">CANCEL</button
|
||||
>
|
||||
<button
|
||||
class="close"
|
||||
id="save"
|
||||
onclick={() => {
|
||||
TabOpen = false;
|
||||
let lines = csv.split("\n");
|
||||
table.Times = lines[0].split(",");
|
||||
table.Monday = lines[1].split(",");
|
||||
table.Tuesday = lines[2].split(",");
|
||||
table.Wednesday = lines[3].split(",");
|
||||
table.Thursday = lines[4].split(",");
|
||||
table.Friday = lines[5].split(",");
|
||||
newTable(table);
|
||||
table = JSON.parse(localStorage.getItem("TimeTable"));
|
||||
}}
|
||||
aria-label="close">SAVE</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
margin: 10px;
|
||||
justify-content: center;
|
||||
background-color: #303030;
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
border-radius: 10px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
display: flex;
|
||||
background-color: #252525;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.Row {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
background-color: #3f3f3f;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.DayOfWeek {
|
||||
width: 100px;
|
||||
background-color: #30492e;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
background-color: #3f3f3f;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.Header > input {
|
||||
background-color: #30492e;
|
||||
}
|
||||
|
||||
#left {
|
||||
gap: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#right {
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#right > button {
|
||||
background-color: rgb(63, 63, 63);
|
||||
color: white;
|
||||
border: 2px solid white;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease-in-out;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
#right > button:hover {
|
||||
transform: scale(1.2);
|
||||
background-color: rgb(50, 50, 50);
|
||||
}
|
||||
|
||||
#UpperLayer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backdrop-filter: blur(10px);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#wrapClose {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.close {
|
||||
background-color: #2b2b2b;
|
||||
color: #888;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
}
|
||||
#box {
|
||||
width: 700px;
|
||||
height: 300px;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
resize: vertical;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
h1,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#options {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#cancel {
|
||||
background-color: #580000;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#save {
|
||||
background-color: #004611;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
231
src/routes/IdleScreen/time.svelte
Normal file
231
src/routes/IdleScreen/time.svelte
Normal file
|
@ -0,0 +1,231 @@
|
|||
<script>
|
||||
import { ShowSeconds } from "./+page.svelte";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let hour1;
|
||||
let hour2;
|
||||
let min1;
|
||||
let min2;
|
||||
let sec1;
|
||||
let sec2;
|
||||
let AmOrPm;
|
||||
|
||||
function DecideScrool(Object, currentTime, legnth, LastZeroPos) {
|
||||
if (currentTime == 0) {
|
||||
if (Object.scrollTop != 0) {
|
||||
Object.scrollTop = LastZeroPos * legnth;
|
||||
setTimeout(() => {
|
||||
Object.scroll({
|
||||
top: 0,
|
||||
behavior: "instant",
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
} else {
|
||||
Object.scrollTop = currentTime * legnth;
|
||||
}
|
||||
}
|
||||
|
||||
function updateTime() {
|
||||
const now = new Date();
|
||||
|
||||
let hours = now.getHours().toString().padStart(2, "0");
|
||||
|
||||
if (now.getHours() >= 12) {
|
||||
hours = (now.getHours() - 12).toString().padStart(2, "0");
|
||||
AmOrPm = "PM";
|
||||
} else {
|
||||
AmOrPm = "AM";
|
||||
}
|
||||
|
||||
const minutes = now.getMinutes().toString().padStart(2, "0");
|
||||
const seconds = now.getSeconds().toString().padStart(2, "0");
|
||||
|
||||
DecideScrool(hour1, parseInt(hours[0]), 200, 2);
|
||||
DecideScrool(hour2, parseInt(hours[1]), 200, 10);
|
||||
DecideScrool(min1, parseInt(minutes[0]), 200, 6);
|
||||
DecideScrool(min2, parseInt(minutes[1]), 200, 10);
|
||||
if (ShowSeconds) {
|
||||
DecideScrool(sec1, parseInt(seconds[0]), 75, 6);
|
||||
DecideScrool(sec2, parseInt(seconds[1]), 75, 10);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
updateTime();
|
||||
setInterval(updateTime, 1000);
|
||||
});
|
||||
|
||||
let TempLocalStorage = localStorage.getItem("ShowSeconds") || "";
|
||||
|
||||
if (TempLocalStorage == "false") {
|
||||
ShowSeconds.v = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div id="wrap">
|
||||
<div id="time">
|
||||
<div class="rowOfNumbers" bind:this={hour1}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
<div class="rowOfNumbers" bind:this={hour2}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>6</h1>
|
||||
<h1>7</h1>
|
||||
<h1>8</h1>
|
||||
<h1>9</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
<h1 id="HourMinDivider">:</h1>
|
||||
<div class="rowOfNumbers" bind:this={min1}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
<div class="rowOfNumbers" bind:this={min2}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>6</h1>
|
||||
<h1>7</h1>
|
||||
<h1>8</h1>
|
||||
<h1>9</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
{#if ShowSeconds.v}
|
||||
<h1 id="MinSecDivider">.</h1>
|
||||
<div class="rowOfNumbersSec" bind:this={sec1}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>0</h1>
|
||||
</div>
|
||||
<div class="rowOfNumbersSec" bind:this={sec2}>
|
||||
<h1>0</h1>
|
||||
<h1>1</h1>
|
||||
<h1>2</h1>
|
||||
<h1>3</h1>
|
||||
<h1>4</h1>
|
||||
<h1>5</h1>
|
||||
<h1>6</h1>
|
||||
<h1>7</h1>
|
||||
<h1>8</h1>
|
||||
<h1>9</h1>
|
||||
<h1>0</h1>
|
||||
</div>{/if}
|
||||
<h1 id="AmOrPm">{AmOrPm}</h1>
|
||||
</div>
|
||||
<div id="date">
|
||||
<h2>
|
||||
{new Date().toLocaleString("en-US", { weekday: "short" })}
|
||||
{new Date().getDate()},
|
||||
{new Date().toLocaleString("en-US", { month: "short" })}
|
||||
{new Date().getFullYear()}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
background-color: #252525;
|
||||
padding: 20px 70px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
#time {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: baseline;
|
||||
margin: -30px;
|
||||
}
|
||||
|
||||
#HourMinDivider {
|
||||
font-size: 200px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#MinSecDivider {
|
||||
font-size: 75px;
|
||||
margin: 0;
|
||||
color: #585858;
|
||||
}
|
||||
|
||||
.rowOfNumbers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 200px;
|
||||
overflow-y: hidden;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.rowOfNumbers h1 {
|
||||
height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.rowOfNumbers > h1 {
|
||||
font-size: 200px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rowOfNumbersSec {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 75px;
|
||||
overflow-y: hidden;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.rowOfNumbersSec > h1 {
|
||||
height: 75px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.rowOfNumbersSec > h1 {
|
||||
font-size: 75px;
|
||||
margin: 0;
|
||||
color: #585858;
|
||||
}
|
||||
|
||||
#AmOrPm {
|
||||
font-size: 75px;
|
||||
margin: 0;
|
||||
margin-left: 30px;
|
||||
color: #585858;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 40px;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
198
src/routes/IdleScreen/timeTable.svelte
Normal file
198
src/routes/IdleScreen/timeTable.svelte
Normal file
|
@ -0,0 +1,198 @@
|
|||
<script module>
|
||||
export function newTable(timetable) {
|
||||
localStorage.setItem("TimeTable", JSON.stringify(timetable));
|
||||
table = timetable;
|
||||
}
|
||||
|
||||
let TemplateTable = {
|
||||
Times: [
|
||||
"07:50 - 08:50",
|
||||
"08:50 - 09:40",
|
||||
"09:40 - 10:30 ",
|
||||
"10:30 - 11:00",
|
||||
"11:00 - 12:00",
|
||||
"12:00 - 01:00",
|
||||
"01:00 - 02:00",
|
||||
],
|
||||
Monday: [
|
||||
"English",
|
||||
"Sanskrit",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
Tuesday: [
|
||||
"English",
|
||||
"Art & Craft",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
Wednesday: [
|
||||
"English",
|
||||
"GK",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
Thursday: [
|
||||
"English",
|
||||
"Sanskrit",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
Friday: [
|
||||
"English",
|
||||
"Computers",
|
||||
"Math",
|
||||
"Lunch",
|
||||
"Hindi",
|
||||
"Social Science",
|
||||
"Science",
|
||||
],
|
||||
};
|
||||
|
||||
let table = $state();
|
||||
|
||||
let TempTimeTable = localStorage.getItem("TimeTable") || "";
|
||||
|
||||
if (TempTimeTable != "") {
|
||||
table = JSON.parse(TempTimeTable);
|
||||
} else {
|
||||
newTable($state.snapshot(TemplateTable));
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div id="wrap">
|
||||
<div class="Header Row">
|
||||
<span class="DayOfWeek" style="Opacity: 0;"></span>
|
||||
{#each table.Times as time}
|
||||
<span>{time}</span>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 1}
|
||||
<span class="DayOfWeek NavHighlight">Monday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Monday</span>
|
||||
{/if}
|
||||
{#each table.Monday as time}
|
||||
{#if new Date().getDay() == 1}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 2}
|
||||
<span class="DayOfWeek NavHighlight">Tuesday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Tuesday</span>
|
||||
{/if}
|
||||
{#each table.Tuesday as time}
|
||||
{#if new Date().getDay() == 2}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 3}
|
||||
<span class="DayOfWeek NavHighlight">Wednesday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Wednesday</span>
|
||||
{/if}
|
||||
{#each table.Wednesday as time}
|
||||
{#if new Date().getDay() == 3}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 4}
|
||||
<span class="DayOfWeek NavHighlight">Thursday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Thursday</span>
|
||||
{/if}
|
||||
{#each table.Thursday as time}
|
||||
{#if new Date().getDay() == 4}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="Row">
|
||||
{#if new Date().getDay() == 5}
|
||||
<span class="DayOfWeek NavHighlight">Friday</span>
|
||||
{:else}
|
||||
<span class="DayOfWeek">Friday</span>
|
||||
{/if}
|
||||
{#each table.Friday as time}
|
||||
{#if new Date().getDay() == 5}
|
||||
<span class="highlight">{time}</span>
|
||||
{:else}
|
||||
<span>{time}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
display: flex;
|
||||
background-color: #252525;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.Row {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
span {
|
||||
font-size: 20px;
|
||||
color: white;
|
||||
background-color: #3f3f3f;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
width: 140px;
|
||||
text-align: center;
|
||||
}
|
||||
.Header > span {
|
||||
background-color: #30492e;
|
||||
}
|
||||
.DayOfWeek {
|
||||
width: 120px;
|
||||
background-color: #30492e;
|
||||
}
|
||||
.highlight {
|
||||
background-color: #707070;
|
||||
display: block;
|
||||
}
|
||||
.NavHighlight {
|
||||
background-color: #3b8235;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
18
src/routes/KahhotClone/+page.svelte
Normal file
18
src/routes/KahhotClone/+page.svelte
Normal file
|
@ -0,0 +1,18 @@
|
|||
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg">
|
||||
<h1 class="m-[0] text-6xl">DaKahootClone</h1>
|
||||
<p class="m-[0] mb-2 text-lg text-gray-400">The best ever kahoot clone.</p>
|
||||
<a href="./join">
|
||||
<button
|
||||
class="cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
>Join a game</button
|
||||
></a
|
||||
>
|
||||
<a href="./create">
|
||||
<button
|
||||
class="cursor-pointer rounded-full bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||
>Create and Host a game</button
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
|
@ -49,7 +49,6 @@
|
|||
<Answers questionsIndex={index} {answersIndex} />
|
||||
{/each}
|
||||
</div>
|
||||
<input type="file">
|
||||
<GenerateOptionsUsingAI {index} />
|
||||
</div>
|
||||
</div>
|
51
src/routes/RandomName/EditNameOfStudents.svelte
Normal file
51
src/routes/RandomName/EditNameOfStudents.svelte
Normal file
|
@ -0,0 +1,51 @@
|
|||
<script>
|
||||
import { newNames, RandomNamesState } from "./main.svelte";
|
||||
|
||||
let names =
|
||||
RandomNamesState.NotSelectedYet.join("\n") +
|
||||
"\n" +
|
||||
RandomNamesState.Selected.join("\n") +
|
||||
"\n" +
|
||||
RandomNamesState.Absent.join("\n");
|
||||
|
||||
let namesArray = [];
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div id="root">
|
||||
<h1>Edit Names</h1>
|
||||
<p></p>
|
||||
<textarea
|
||||
bind:value={names}
|
||||
id="box"
|
||||
onchange={() => {
|
||||
let namesArray = names.split("\n");
|
||||
newNames(namesArray);
|
||||
}}
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
margin: 10px;
|
||||
justify-content: center;
|
||||
background-color: #303030;
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#box {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
resize: vertical;
|
||||
font-size: 20px;
|
||||
}
|
||||
</style>
|
163
src/routes/RandomName/TopDisplay.svelte
Normal file
163
src/routes/RandomName/TopDisplay.svelte
Normal file
|
@ -0,0 +1,163 @@
|
|||
<script context="module">
|
||||
import { RandomNamesState } from "./main.svelte";
|
||||
|
||||
let DragTempName = "na";
|
||||
|
||||
import "drag-drop-touch";
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div
|
||||
id="Selected"
|
||||
on:dragover|preventDefault
|
||||
role="list"
|
||||
aria-label="drag name to this list!"
|
||||
on:drop={(event) => {
|
||||
event.preventDefault();
|
||||
if (DragTempName !== "na") {
|
||||
RandomNamesState.Selected.push(DragTempName);
|
||||
DragTempName = "na";
|
||||
}
|
||||
}}
|
||||
>
|
||||
<h1>Selected</h1>
|
||||
<div class="wrap">
|
||||
{#each RandomNamesState.Selected as name, i}
|
||||
<span
|
||||
class="Selected"
|
||||
draggable="true"
|
||||
role="listitem"
|
||||
aria-label="drag name to different list!"
|
||||
on:dragstart={() => {
|
||||
setTimeout(() => {
|
||||
RandomNamesState.Selected.splice(i, 1);
|
||||
DragTempName = name;
|
||||
}, 100);
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div
|
||||
id="NotSelectedYet"
|
||||
on:dragover|preventDefault
|
||||
role="list"
|
||||
aria-label="drag name to this list!"
|
||||
on:drop={(event) => {
|
||||
event.preventDefault();
|
||||
if (DragTempName !== "na") {
|
||||
RandomNamesState.NotSelectedYet.push(DragTempName);
|
||||
DragTempName = "na";
|
||||
}
|
||||
}}
|
||||
>
|
||||
<h1>Not Selected Yet</h1>
|
||||
<div class="wrap">
|
||||
{#each RandomNamesState.NotSelectedYet as name, i}
|
||||
<span
|
||||
class="NotSelectedYet"
|
||||
draggable="true"
|
||||
role="listitem"
|
||||
aria-label="drag name to different list!"
|
||||
on:dragstart={() => {
|
||||
setTimeout(() => {
|
||||
RandomNamesState.NotSelectedYet.splice(i, 1);
|
||||
DragTempName = name;
|
||||
}, 100);
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div
|
||||
id="Absent"
|
||||
on:dragover|preventDefault
|
||||
role="list"
|
||||
aria-label="drag name to this list!"
|
||||
on:drop={(event) => {
|
||||
event.preventDefault();
|
||||
if (DragTempName !== "na") {
|
||||
RandomNamesState.Absent.push(DragTempName);
|
||||
DragTempName = "na";
|
||||
}
|
||||
}}
|
||||
>
|
||||
<h1>Absent</h1>
|
||||
<div class="wrap">
|
||||
{#each RandomNamesState.Absent as name, i}
|
||||
<span
|
||||
draggable="true"
|
||||
class="Absent"
|
||||
role="listitem"
|
||||
aria-label="drag name to different list!"
|
||||
on:dragstart={() => {
|
||||
setTimeout(() => {
|
||||
RandomNamesState.Absent.splice(i, 1);
|
||||
DragTempName = name;
|
||||
}, 100);
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
display: flex;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
background-color: #4d4d4d;
|
||||
color: white;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#NotSelectedYet,
|
||||
#Selected {
|
||||
min-width: 40%;
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
#Absent {
|
||||
min-width: 20%;
|
||||
max-width: 20%;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: fit-content;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.NotSelectedYet {
|
||||
color: #ffe677;
|
||||
}
|
||||
|
||||
.Selected {
|
||||
color: #a3ffa3;
|
||||
}
|
||||
|
||||
.Absent {
|
||||
color: #242424;
|
||||
}
|
||||
</style>
|
220
src/routes/RandomName/main.svelte
Normal file
220
src/routes/RandomName/main.svelte
Normal file
|
@ -0,0 +1,220 @@
|
|||
<script module>
|
||||
import Selector from "./selector.svelte";
|
||||
import TopDisplay from "./TopDisplay.svelte";
|
||||
import EditNameOfStudents from "./EditNameOfStudents.svelte";
|
||||
import {ShowAlert} from "../+page.svelte";
|
||||
|
||||
export let RandomNamesState = $state({
|
||||
NotSelectedYet: [],
|
||||
Selected: [],
|
||||
Absent: [],
|
||||
selectedStudent: "None yet!",
|
||||
});
|
||||
|
||||
export function SelectStudent() {
|
||||
if (RandomNamesState.NotSelectedYet.length != 0) {
|
||||
let randomIndex = Math.floor(
|
||||
Math.random() * RandomNamesState.NotSelectedYet.length
|
||||
);
|
||||
RandomNamesState.selectedStudent =
|
||||
RandomNamesState.NotSelectedYet[randomIndex];
|
||||
|
||||
RandomNamesState.Selected = [
|
||||
RandomNamesState.NotSelectedYet[randomIndex],
|
||||
...RandomNamesState.Selected,
|
||||
];
|
||||
RandomNamesState.NotSelectedYet.splice(randomIndex, 1);
|
||||
} else {
|
||||
ShowAlert("All students have been selected.", "warning");
|
||||
}
|
||||
}
|
||||
|
||||
let TabOpen = $state({
|
||||
v: false,
|
||||
});
|
||||
|
||||
export function newNames(Names) {
|
||||
localStorage.setItem("Names", JSON.stringify(Names));
|
||||
RandomNamesState.NotSelectedYet = Names;
|
||||
RandomNamesState.Selected = [];
|
||||
RandomNamesState.Absent = [];
|
||||
RandomNamesState.selectedStudent = "None yet!";
|
||||
}
|
||||
|
||||
let TempelateNames = ["John", "Jane", "Alice", "Bob"];
|
||||
|
||||
let TempNames = localStorage.getItem("Names") || "";
|
||||
|
||||
if (TempNames != "") {
|
||||
RandomNamesState.NotSelectedYet = JSON.parse(TempNames);
|
||||
RandomNamesState.Selected = [];
|
||||
RandomNamesState.Absent = [];
|
||||
RandomNamesState.selectedStudent = "None yet!";
|
||||
} else {
|
||||
newNames($state.snapshot(TempelateNames));
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="wrap">
|
||||
<div id="nav">
|
||||
<a href="#/" aria-label="Back to main menu"
|
||||
><button aria-label="Back to main menu"
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"
|
||||
/></svg
|
||||
> GO BACK
|
||||
</span></button
|
||||
></a
|
||||
>
|
||||
<h1>Random student selector</h1>
|
||||
<div>
|
||||
<button
|
||||
aria-label="Back to main menu"
|
||||
onclick={() => (TabOpen.v = true)}
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M400-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM80-160v-112q0-33 17-62t47-44q51-26 115-44t141-18h14q6 0 12 2-8 18-13.5 37.5T404-360h-4q-71 0-127.5 18T180-306q-9 5-14.5 14t-5.5 20v32h252q6 21 16 41.5t22 38.5H80Zm560 40-12-60q-12-5-22.5-10.5T584-204l-58 18-40-68 46-40q-2-14-2-26t2-26l-46-40 40-68 58 18q11-8 21.5-13.5T628-460l12-60h80l12 60q12 5 22.5 11t21.5 15l58-20 40 70-46 40q2 12 2 25t-2 25l46 40-40 68-58-18q-11 8-21.5 13.5T732-180l-12 60h-80Zm40-120q33 0 56.5-23.5T760-320q0-33-23.5-56.5T680-400q-33 0-56.5 23.5T600-320q0 33 23.5 56.5T680-240ZM400-560q33 0 56.5-23.5T480-640q0-33-23.5-56.5T400-720q-33 0-56.5 23.5T320-640q0 33 23.5 56.5T400-560Zm0-80Zm12 400Z"
|
||||
/></svg
|
||||
> Edit list of names
|
||||
</span></button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div id="root">
|
||||
<Selector />
|
||||
<div id="listWrap"><TopDisplay /></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if TabOpen.v !== false}
|
||||
<div id="UperLayer">
|
||||
<div id="wrapClose">
|
||||
<EditNameOfStudents />
|
||||
<button
|
||||
class="close"
|
||||
onclick={() => (TabOpen.v = false)}
|
||||
aria-label="close">CLOSE</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
#wrap {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #292929;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
margin: 10px;
|
||||
padding: 2px 0px;
|
||||
cursor: pointer;
|
||||
outline-offset: 4px;
|
||||
max-width: 400px;
|
||||
}
|
||||
button:hover .front {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
|
||||
button:active .front {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.front {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
border-radius: 12px;
|
||||
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-4px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
#root {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 5px 0px;
|
||||
text-decoration: underline #444;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.5em;
|
||||
text-decoration: none;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
#listWrap {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
#UperLayer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backdrop-filter: blur(5px);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#wrapClose {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.close {
|
||||
background-color: #2b2b2b;
|
||||
color: #888;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
135
src/routes/RandomName/selector.svelte
Normal file
135
src/routes/RandomName/selector.svelte
Normal file
|
@ -0,0 +1,135 @@
|
|||
<script>
|
||||
import { RandomNamesState, SelectStudent } from "./main.svelte";
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<p>The latest selected student is:</p>
|
||||
<h1>{RandomNamesState.selectedStudent}</h1>
|
||||
|
||||
<div id="options">
|
||||
<button
|
||||
aria-label="Back to main menu"
|
||||
id="selectStudent"
|
||||
onclick={() => {
|
||||
SelectStudent();
|
||||
}}
|
||||
><span class="front">
|
||||
<span>Select a random student</span>
|
||||
</span></button
|
||||
>
|
||||
<div>
|
||||
{#if RandomNamesState.Selected.length > 0}
|
||||
<button
|
||||
aria-label="Back to main menu"
|
||||
onclick={() => {
|
||||
if (
|
||||
confirm(
|
||||
"Are you sure you want to reset all the Selected names? This action cannot be undone."
|
||||
)
|
||||
) {
|
||||
RandomNamesState.NotSelectedYet = [
|
||||
...RandomNamesState.NotSelectedYet,
|
||||
...RandomNamesState.Selected,
|
||||
];
|
||||
RandomNamesState.Selected = [];
|
||||
RandomNamesState.selectedStudent = "None yet!";
|
||||
}
|
||||
}}
|
||||
><span class="front">
|
||||
<span>Reset selected</span>
|
||||
</span></button
|
||||
>{/if}
|
||||
{#if RandomNamesState.Absent.length > 0}
|
||||
<button
|
||||
aria-label="Back to main menu"
|
||||
onclick={() => {
|
||||
if (
|
||||
confirm(
|
||||
"Are you sure you want to reset all the names? this will also move all the students to the 'Not Selected Yet' list. This action cannot be undone."
|
||||
)
|
||||
) {
|
||||
RandomNamesState.NotSelectedYet = [
|
||||
...RandomNamesState.NotSelectedYet,
|
||||
...RandomNamesState.Absent,
|
||||
];
|
||||
RandomNamesState.Absent = [];
|
||||
RandomNamesState.selectedStudent = "None yet!";
|
||||
}
|
||||
}}
|
||||
><span class="front">
|
||||
<span>Reset absentees</span>
|
||||
</span></button
|
||||
>{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
background-color: #222;
|
||||
border-radius: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: 20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-bottom: 0px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
#options {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #292929;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
margin: 10px;
|
||||
padding: 2px 0px;
|
||||
cursor: pointer;
|
||||
outline-offset: 4px;
|
||||
width: fit-content;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
button:hover .front {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
|
||||
button:active .front {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.front {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
border-radius: 12px;
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-4px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 25px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 120px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #aaa;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#selectStudent > .front {
|
||||
background-color: #0e71e2;
|
||||
}
|
||||
</style>
|
182
src/routes/announcer/ActualAnnouncer.svelte
Normal file
182
src/routes/announcer/ActualAnnouncer.svelte
Normal file
|
@ -0,0 +1,182 @@
|
|||
<script>
|
||||
let text = $state("");
|
||||
|
||||
function speak() {
|
||||
window.speechSynthesis.speak(
|
||||
Object.assign(new SpeechSynthesisUtterance(text), {
|
||||
rate: 0.5,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
let TempelateCommonAnounce = $state(["Please be quiet"]);
|
||||
|
||||
let CommonAnounce = $state([]);
|
||||
|
||||
export function newNames(Names) {
|
||||
localStorage.setItem("CommonAnounce", JSON.stringify(Names));
|
||||
}
|
||||
|
||||
let TempCommonAnounce = localStorage.getItem("CommonAnounce") || "";
|
||||
|
||||
if (TempCommonAnounce != "") {
|
||||
CommonAnounce = JSON.parse(TempCommonAnounce);
|
||||
} else {
|
||||
CommonAnounce = $state.snapshot(TempelateCommonAnounce);
|
||||
localStorage.setItem("CommonAnounce", JSON.stringify(CommonAnounce));
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div id="wrap">
|
||||
<h1>Most Announced announcements</h1>
|
||||
|
||||
<div id="mostanouncedanounements">
|
||||
{#each CommonAnounce as anouncement, i}
|
||||
<div>
|
||||
<button
|
||||
class="anuncement"
|
||||
onclick={() => {
|
||||
text = anouncement;
|
||||
speak();
|
||||
}}
|
||||
>
|
||||
{anouncement}
|
||||
</button>
|
||||
<button
|
||||
aria-label="Delete anouncement"
|
||||
onclick={() => {
|
||||
if (
|
||||
confirm(
|
||||
"Are you sure you want to delete this anouncement?"
|
||||
)
|
||||
) {
|
||||
CommonAnounce.splice(i, 1);
|
||||
localStorage.setItem(
|
||||
"CommonAnounce",
|
||||
JSON.stringify(CommonAnounce)
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="15px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="15px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"
|
||||
/></svg
|
||||
></button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<h1>Or announce something else</h1>
|
||||
<div>
|
||||
<input
|
||||
bind:value={text}
|
||||
placeholder="Type in here what you want to announce"
|
||||
/>
|
||||
<button onclick={speak}>Play</button>
|
||||
</div>
|
||||
{#if text}
|
||||
<button
|
||||
id="new"
|
||||
onclick={() => {
|
||||
CommonAnounce.push(text);
|
||||
text = "";
|
||||
localStorage.setItem(
|
||||
"CommonAnounce",
|
||||
JSON.stringify(CommonAnounce)
|
||||
);
|
||||
}}>Add "{text}" to "Most Announced announcements"</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
background-color: #292929;
|
||||
color: white;
|
||||
width: 400px;
|
||||
}
|
||||
button {
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
background-color: #292929;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #414141;
|
||||
transform: rotateZ(-15deg) scale(1.2);
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #292929;
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
#mostanouncedanounements {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.anuncement:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.anuncement {
|
||||
width: calc(100% - 40px);
|
||||
}
|
||||
|
||||
#new {
|
||||
width: fit-content;
|
||||
align-self: center;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#new:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
100
src/routes/announcer/main.svelte
Normal file
100
src/routes/announcer/main.svelte
Normal file
|
@ -0,0 +1,100 @@
|
|||
<script>
|
||||
import ActualAnnouncer from "./ActualAnnouncer.svelte";
|
||||
</script>
|
||||
|
||||
<div id="wrap">
|
||||
<div id="nav">
|
||||
<a href="#/" aria-label="Back to main menu"
|
||||
><button aria-label="Back to main menu"
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"
|
||||
/></svg
|
||||
> GO BACK
|
||||
</span></button
|
||||
></a
|
||||
>
|
||||
<h1>Announcer</h1>
|
||||
</div>
|
||||
<div id="root">
|
||||
<ActualAnnouncer />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#wrap {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #292929;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
margin: 10px;
|
||||
padding: 2px 0px;
|
||||
cursor: pointer;
|
||||
outline-offset: 4px;
|
||||
max-width: 400px;
|
||||
}
|
||||
button:hover .front {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
|
||||
button:active .front {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.front {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
border-radius: 12px;
|
||||
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-4px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
#root {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 5px 0px;
|
||||
text-decoration: underline #444;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.5em;
|
||||
text-decoration: none;
|
||||
color: #444;
|
||||
}
|
||||
</style>
|
45
src/routes/wordle/InfoAndSetings/WordLegnthSetings.svelte
Normal file
45
src/routes/wordle/InfoAndSetings/WordLegnthSetings.svelte
Normal file
|
@ -0,0 +1,45 @@
|
|||
<script>
|
||||
import { newGame, WordLegnth } from "../logic.svelte.js";
|
||||
import { TabOpen } from "./main.svelte";
|
||||
|
||||
let LetersSelected = WordLegnth.v;
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<h4>Select The Word legnth (between 3 and 10 letters)</h4>
|
||||
<input
|
||||
type="range"
|
||||
id="vol"
|
||||
name="vol"
|
||||
min="3"
|
||||
max="10"
|
||||
bind:value={LetersSelected}
|
||||
/>
|
||||
<button
|
||||
onclick={() => {
|
||||
WordLegnth.v = LetersSelected;
|
||||
newGame();
|
||||
TabOpen.v = "none";
|
||||
}}
|
||||
>Start New Game with Word legnth of {LetersSelected} letters
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
margin: 10px;
|
||||
justify-content: center;
|
||||
background-color: #303030;
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
border-radius: 10px;
|
||||
width: 400px;
|
||||
}
|
||||
h4 {
|
||||
text-align: center;
|
||||
margin: 0px;
|
||||
}
|
||||
</style>
|
58
src/routes/wordle/InfoAndSetings/main.svelte
Normal file
58
src/routes/wordle/InfoAndSetings/main.svelte
Normal file
|
@ -0,0 +1,58 @@
|
|||
<script module>
|
||||
import { newGame, WordLegnth } from "../logic.svelte.js";
|
||||
import WordLegnthSetings from "./WordLegnthSetings.svelte";
|
||||
import Stats from "./stats.svelte";
|
||||
|
||||
export let TabOpen = $state({ v: "none" });
|
||||
|
||||
export function OpenTab(type) {
|
||||
TabOpen.v = type;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if TabOpen.v !== "none"}
|
||||
<div id="UperLayer">
|
||||
<div id="wrap">
|
||||
{#if TabOpen.v == "WordLength"}
|
||||
<WordLegnthSetings />
|
||||
{:else if TabOpen.v == "Stats"}
|
||||
<Stats />
|
||||
{/if}
|
||||
<button
|
||||
class="close"
|
||||
onclick={() => (TabOpen.v = "none")}
|
||||
aria-label="close">CLOSE</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
#UperLayer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backdrop-filter: blur(5px);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.close {
|
||||
background-color: #2b2b2b;
|
||||
color: #888;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
155
src/routes/wordle/InfoAndSetings/stats.svelte
Normal file
155
src/routes/wordle/InfoAndSetings/stats.svelte
Normal file
|
@ -0,0 +1,155 @@
|
|||
<script>
|
||||
import { data, WordLegnth } from "../logic.svelte.js";
|
||||
|
||||
// svelte-ignore non_reactive_update
|
||||
let LetersSelected = JSON.stringify(WordLegnth.v);
|
||||
|
||||
let dataPoints = data.value[LetersSelected];
|
||||
|
||||
import { onMount } from "svelte";
|
||||
import Chart from "chart.js/auto";
|
||||
|
||||
let canvas;
|
||||
let chart;
|
||||
|
||||
onMount(() => {
|
||||
const ctx = canvas.getContext("2d");
|
||||
chart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: dataPoints.map((_, i) => `Game ${i + 1}`),
|
||||
datasets: [
|
||||
{
|
||||
label: "Number Of Guesses Taken to Win",
|
||||
data: $state.snapshot(dataPoints),
|
||||
borderColor: "rgba(75, 192, 192, 1)",
|
||||
backgroundColor: "rgba(75, 192, 192, 0.2)",
|
||||
fill: true,
|
||||
tension: 0.3,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
let Avgguesses = $state(
|
||||
(
|
||||
dataPoints.reduce((acc, val) => acc + val, 0) / dataPoints.length
|
||||
).toFixed(2)
|
||||
);
|
||||
let TotalWins = $state(dataPoints.length);
|
||||
|
||||
$effect(() => {
|
||||
const snapshot = JSON.stringify(data.value);
|
||||
UpdateChart(data.value);
|
||||
});
|
||||
|
||||
function UpdateChart(val) {
|
||||
dataPoints = data.value[LetersSelected];
|
||||
if (chart) {
|
||||
chart.data.labels = dataPoints.map((_, i) => `Game ${i + 1}`);
|
||||
chart.data.datasets[0].data = $state.snapshot(dataPoints);
|
||||
chart.update();
|
||||
}
|
||||
Avgguesses = (
|
||||
dataPoints.reduce((acc, val) => acc + val, 0) / dataPoints.length
|
||||
).toFixed(2);
|
||||
TotalWins = dataPoints.length;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<p>
|
||||
Stats For:
|
||||
<select
|
||||
bind:value={LetersSelected}
|
||||
onchange={() => {
|
||||
dataPoints = data.value[LetersSelected];
|
||||
if (chart) {
|
||||
chart.data.labels = dataPoints.map(
|
||||
(_, i) => `Point ${i + 1}`
|
||||
);
|
||||
chart.data.datasets[0].data = $state.snapshot(dataPoints);
|
||||
chart.update();
|
||||
}
|
||||
Avgguesses = (
|
||||
dataPoints.reduce((acc, val) => acc + val, 0) /
|
||||
dataPoints.length
|
||||
).toFixed(2);
|
||||
TotalWins = dataPoints.length;
|
||||
}}
|
||||
>
|
||||
<option value="3">3 Letters</option>
|
||||
<option value="4">4 Letters</option>
|
||||
<option value="5" selected>5 Letters</option>
|
||||
<option value="6">6 Letters</option>
|
||||
<option value="7">7 Letters</option>
|
||||
<option value="8">8 Letters</option>
|
||||
<option value="9">9 Letters</option>
|
||||
<option value="10">10 Letters</option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<div id="toprow">
|
||||
<div class="toprow">
|
||||
<h2>Total WINS</h2>
|
||||
<h1>{TotalWins}</h1>
|
||||
</div>
|
||||
<div class="toprow">
|
||||
<h2>Avg No. of guesses</h2>
|
||||
<h1>{Avgguesses}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<canvas bind:this={canvas}></canvas>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
margin: 10px;
|
||||
justify-content: center;
|
||||
background-color: #303030;
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
border-radius: 10px;
|
||||
width: 700px;
|
||||
}
|
||||
h2,
|
||||
h1,
|
||||
p {
|
||||
text-align: center;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#toprow {
|
||||
display: inline-flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.toprow {
|
||||
background-color: #3f3f3f;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.toprow:nth-child(1n) {
|
||||
min-width: 35%;
|
||||
}
|
||||
.toprow:nth-child(2n) {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
148
src/routes/wordle/game.svelte
Normal file
148
src/routes/wordle/game.svelte
Normal file
|
@ -0,0 +1,148 @@
|
|||
<script>
|
||||
import Keyboard from "./game/keyboard.svelte";
|
||||
import Display from "./game/display.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { handleKey } from "./logic.svelte.js";
|
||||
import Right from "./InfoAndSetings/main.svelte";
|
||||
import { OpenTab } from "./InfoAndSetings/main.svelte";
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener("keydown", handleKey);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="nav">
|
||||
<a href="#/" aria-label="Back to main menu"
|
||||
><button aria-label="Back to main menu"
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"
|
||||
/></svg
|
||||
> GO BACK
|
||||
</span></button
|
||||
></a
|
||||
>
|
||||
<h1>Wordle</h1>
|
||||
<div>
|
||||
<button
|
||||
aria-label="Back to main menu"
|
||||
onclick={() => OpenTab("WordLength")}
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="M320-80 160-240l160-160 57 56-64 64h334l-63-64 56-56 160 160L640-80l-57-56 64-64H313l63 64-56 56ZM200-480v-400h80v400h-80Zm240 0v-400h80v400h-80Zm240 0v-400h80v400h-80Z"
|
||||
/></svg
|
||||
> Change word legnth
|
||||
</span></button
|
||||
>
|
||||
<button aria-label="Back to main menu" onclick={() => OpenTab("Stats")}
|
||||
><span class="front"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24px"
|
||||
fill="#FFFFFF"
|
||||
><path
|
||||
d="m105-399-65-47 200-320 120 140 160-260 120 180 135-214 65 47-198 314-119-179-152 247-121-141-145 233Zm475 159q42 0 71-29t29-71q0-42-29-71t-71-29q-42 0-71 29t-29 71q0 42 29 71t71 29ZM784-80 676-188q-21 14-45.5 21t-50.5 7q-75 0-127.5-52.5T400-340q0-75 52.5-127.5T580-520q75 0 127.5 52.5T760-340q0 26-7 50.5T732-244l108 108-56 56Z"
|
||||
/></svg
|
||||
> Stats
|
||||
</span></button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div id="root">
|
||||
<div id="left">
|
||||
<Display />
|
||||
<Keyboard />
|
||||
</div>
|
||||
<Right />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #292929;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
margin: 10px;
|
||||
padding: 2px 0px;
|
||||
cursor: pointer;
|
||||
outline-offset: 4px;
|
||||
max-width: 400px;
|
||||
}
|
||||
button:hover .front {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
|
||||
button:active .front {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.front {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
border-radius: 12px;
|
||||
|
||||
background: #4d4d4d;
|
||||
color: white;
|
||||
transform: translateY(-4px);
|
||||
transition: all 0.1s ease-in-out;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 90%;
|
||||
display: flex;
|
||||
margin: 0;
|
||||
font-family: "Sour Gummy", sans-serif;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#left {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 20px;
|
||||
margin: 20px;
|
||||
margin-top: 5px;
|
||||
border: 2px solid #444;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 5px 0px;
|
||||
text-decoration: underline #444;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.5em;
|
||||
text-decoration: none;
|
||||
color: #444;
|
||||
}
|
||||
</style>
|
73
src/routes/wordle/game/display.svelte
Normal file
73
src/routes/wordle/game/display.svelte
Normal file
|
@ -0,0 +1,73 @@
|
|||
<script>
|
||||
import { CurrentWord, words, WordLegnth } from "../logic.svelte.js";
|
||||
</script>
|
||||
|
||||
<div id="DisplayOfWords">
|
||||
{#each words.v as word}
|
||||
<div class="word">
|
||||
{#each word as letter}
|
||||
<span class={letter[1]}>{letter[0]}</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
<div class="word">
|
||||
{#each Array(WordLegnth.v) as _, i}
|
||||
<span>{CurrentWord.v[i] || ""}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#DisplayOfWords {
|
||||
height: calc(100% - 320px);
|
||||
border: 2px solid #202020;
|
||||
margin: 20px;
|
||||
border-radius: 10px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.word {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
margin: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 10px;
|
||||
font-size: 50px;
|
||||
border: 2px solid #444;
|
||||
}
|
||||
|
||||
.c {
|
||||
background-color: #2b5f2d;
|
||||
}
|
||||
.d {
|
||||
background-color: #804d00;
|
||||
}
|
||||
.w {
|
||||
background-color: #2b2b2b;
|
||||
}
|
||||
|
||||
/* width */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
border: 1px solid #5c5c5c;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #3f3f3f;
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
71
src/routes/wordle/game/keyboard.svelte
Normal file
71
src/routes/wordle/game/keyboard.svelte
Normal file
|
@ -0,0 +1,71 @@
|
|||
<script>
|
||||
import { keys, ButtonPressed } from "../logic.svelte.js";
|
||||
</script>
|
||||
|
||||
<div id="root">
|
||||
<div class="word">
|
||||
{#each keys.v.slice(0, 10) as key}
|
||||
<button on:click={() => ButtonPressed(key[0])} class={key[1]}
|
||||
>{key[0]}</button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="word">
|
||||
{#each keys.v.slice(10, 19) as key}
|
||||
<button on:click={() => ButtonPressed(key[0])} class={key[1]}
|
||||
>{key[0]}</button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="word">
|
||||
{#each keys.v.slice(19) as key}
|
||||
<button on:click={() => ButtonPressed(key[0])} class={key[1]}
|
||||
>{key[0]}</button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#root {
|
||||
height: 255px;
|
||||
margin: 20px;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #202020;
|
||||
}
|
||||
|
||||
.word {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
margin: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
button {
|
||||
background: none;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
color: #888;
|
||||
width: 80px;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 10px;
|
||||
font-size: 50px;
|
||||
border: 2px solid #444;
|
||||
}
|
||||
|
||||
.c {
|
||||
background-color: #2b5f2d;
|
||||
}
|
||||
.d {
|
||||
background-color: #804d00;
|
||||
}
|
||||
.w {
|
||||
background-color: #2b2b2b;
|
||||
}
|
||||
.o {
|
||||
background-color: #00202c;
|
||||
}
|
||||
</style>
|
220
src/routes/wordle/logic.svelte.js
Normal file
220
src/routes/wordle/logic.svelte.js
Normal file
|
@ -0,0 +1,220 @@
|
|||
import wordExists from "word-exists";
|
||||
import { generate } from "random-words";
|
||||
import { ShowAlert } from "../+layout.svelte";
|
||||
|
||||
export let WordLegnth = $state({ v: 5 });
|
||||
|
||||
let CorrectWord = generate({
|
||||
minLength: WordLegnth.v,
|
||||
maxLength: WordLegnth.v,
|
||||
});
|
||||
|
||||
console.log("CorrectWord: ", CorrectWord);
|
||||
export let words = $state({ v: [] });
|
||||
export let CurrentWord = $state({ v: [] });
|
||||
export let keys = $state({
|
||||
v: [
|
||||
["Q", "n"],
|
||||
["W", "n"],
|
||||
["E", "n"],
|
||||
["R", "n"],
|
||||
["T", "n"],
|
||||
["Y", "n"],
|
||||
["U", "n"],
|
||||
["I", "n"],
|
||||
["O", "n"],
|
||||
["P", "n"],
|
||||
["A", "n"],
|
||||
["S", "n"],
|
||||
["D", "n"],
|
||||
["F", "n"],
|
||||
["G", "n"],
|
||||
["H", "n"],
|
||||
["J", "n"],
|
||||
["K", "n"],
|
||||
["L", "n"],
|
||||
["⌫", "o"],
|
||||
["Z", "n"],
|
||||
["X", "n"],
|
||||
["C", "n"],
|
||||
["V", "n"],
|
||||
["B", "n"],
|
||||
["N", "n"],
|
||||
["M", "n"],
|
||||
["⏎", "o"],
|
||||
],
|
||||
});
|
||||
|
||||
export function newGame() {
|
||||
CorrectWord = generate({
|
||||
minLength: WordLegnth.v,
|
||||
maxLength: WordLegnth.v,
|
||||
});
|
||||
console.log("CorrectWord: ", CorrectWord);
|
||||
words.v = [];
|
||||
CurrentWord.v = [];
|
||||
keys.v = [
|
||||
["Q", "n"],
|
||||
["W", "n"],
|
||||
["E", "n"],
|
||||
["R", "n"],
|
||||
["T", "n"],
|
||||
["Y", "n"],
|
||||
["U", "n"],
|
||||
["I", "n"],
|
||||
["O", "n"],
|
||||
["P", "n"],
|
||||
["A", "n"],
|
||||
["S", "n"],
|
||||
["D", "n"],
|
||||
["F", "n"],
|
||||
["G", "n"],
|
||||
["H", "n"],
|
||||
["J", "n"],
|
||||
["K", "n"],
|
||||
["L", "n"],
|
||||
["⌫", "o"],
|
||||
["Z", "n"],
|
||||
["X", "n"],
|
||||
["C", "n"],
|
||||
["V", "n"],
|
||||
["B", "n"],
|
||||
["N", "n"],
|
||||
["M", "n"],
|
||||
["⏎", "o"],
|
||||
];
|
||||
}
|
||||
|
||||
function GameWin() {
|
||||
ShowAlert("You win!", "success");
|
||||
data.value[WordLegnth.v].push(words.v.length);
|
||||
localStorage.setItem("WordleGamesData", JSON.stringify(data.value));
|
||||
newGame();
|
||||
}
|
||||
|
||||
function SendWord(word) {
|
||||
let result = Array(word.length).fill(null);
|
||||
let used = Array(CorrectWord.length).fill(false);
|
||||
|
||||
// First pass: exact matches
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
if (word[i].toLowerCase() === CorrectWord[i].toLowerCase()) {
|
||||
result[i] = [word[i].toUpperCase(), "c"];
|
||||
used[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: wrong place but correct letter
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
if (result[i]) continue;
|
||||
|
||||
let found = false;
|
||||
for (let j = 0; j < CorrectWord.length; j++) {
|
||||
if (!used[j] && word[i].toLowerCase() === CorrectWord[j].toLowerCase()) {
|
||||
found = true;
|
||||
used[j] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result[i] = found ? [word[i].toUpperCase(), "d"] : [word[i].toUpperCase(), "w"];
|
||||
}
|
||||
|
||||
words.v.push(result);
|
||||
|
||||
setTimeout(() => {
|
||||
document.getElementById("DisplayOfWords").scrollTo({
|
||||
top: document.getElementById("DisplayOfWords").scrollHeight,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, 100);
|
||||
|
||||
// Update keyboard status
|
||||
for (let [letter, status] of result) {
|
||||
let keyIndex = keys.v.findIndex((k) => k[0].toLowerCase() === letter.toLowerCase());
|
||||
if (keyIndex !== -1) {
|
||||
let current = keys.v[keyIndex][1];
|
||||
if (status === "c") {
|
||||
keys.v[keyIndex][1] = "c";
|
||||
} else if (status === "d" && current === "n") {
|
||||
keys.v[keyIndex][1] = "d";
|
||||
} else if (status === "w" && current === "n") {
|
||||
keys.v[keyIndex][1] = "w";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for win
|
||||
if (result.every(([_, status]) => status === "c")) {
|
||||
GameWin();
|
||||
}
|
||||
}
|
||||
|
||||
export function ButtonPressed(key) {
|
||||
document.getElementById("DisplayOfWords").scrollTo({
|
||||
top: document.getElementById("DisplayOfWords").scrollHeight,
|
||||
behavior: "smooth",
|
||||
});
|
||||
|
||||
if (key === "⏎") {
|
||||
if (CurrentWord.v.length === WordLegnth.v) {
|
||||
let word = CurrentWord.v.join("").toUpperCase();
|
||||
if (wordExists(word)) {
|
||||
SendWord(CurrentWord.v);
|
||||
CurrentWord.v = [];
|
||||
} else {
|
||||
ShowAlert("Not a valid word", "error");
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (key === "⌫") {
|
||||
CurrentWord.v.pop();
|
||||
return;
|
||||
}
|
||||
if (CurrentWord.v.length === WordLegnth.v) {
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentWord.v.push(key);
|
||||
}
|
||||
export function handleKey(event) {
|
||||
const key = event.key.toLowerCase();
|
||||
|
||||
if (key === "enter") {
|
||||
ButtonPressed("⏎");
|
||||
} else if (key === "backspace") {
|
||||
ButtonPressed("⌫");
|
||||
} else if (/^[a-z]$/.test(key)) {
|
||||
ButtonPressed(key.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
export let data = $state({
|
||||
value: {
|
||||
3: [],
|
||||
4: [],
|
||||
5: [],
|
||||
6: [],
|
||||
7: [],
|
||||
8: [],
|
||||
9: [],
|
||||
10: [],
|
||||
},
|
||||
});
|
||||
|
||||
let WordleGamesData = localStorage.getItem("WordleGamesData") || "";
|
||||
|
||||
if (WordleGamesData != "") {
|
||||
data.value = JSON.parse(WordleGamesData);
|
||||
} else {
|
||||
data.value = {
|
||||
3: [],
|
||||
4: [],
|
||||
5: [],
|
||||
6: [],
|
||||
7: [],
|
||||
8: [],
|
||||
9: [],
|
||||
10: [],
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue