This commit is contained in:
Daniel Bulant 2023-01-21 18:13:45 +01:00
parent af3d0481ca
commit 75ba376f46
11 changed files with 181 additions and 44 deletions

View file

@ -0,0 +1,9 @@
<script>
import { themeStore } from "./themeStore";
</script>
{#if $themeStore == "dark"}
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="white"><rect fill="none" height="24" width="24"/><path d="M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0 c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2 c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1 C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06 c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41 l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41 c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36 c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>
{:else}
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36c-0.98,1.37-2.58,2.26-4.4,2.26 c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>
{/if}

View file

@ -168,7 +168,7 @@
}
</script>
<a href="/" on:click={check} class="arrow-back fixed top-0 left-0 w-4 h-4 m-4 p-2 transform transition-transform hover:-translate-x-1">
<a href="/" on:click={check} class="text-black dark:text-white arrow-back fixed top-0 left-0 w-4 h-4 m-4 p-2 transform transition-transform hover:-translate-x-1">
<svg width="16" height="16">
<line y1="50%" x1="0" y2="50%" x2="100%" stroke="currentColor" stroke-width="2" />
<line y1="50%" x1="0" y2="100%" x2="50%" stroke="currentColor" stroke-width="2" />
@ -177,7 +177,7 @@
</a>
{#if !twoPlayer}
<div on:click={reset} class="reload fixed top-0 left-10 w-4 h-4 m-4 p-2 transform transition-transform rotate-180 hover:rotate-360 active:rotate-540">
<div on:click={reset} on:keydown={reset} class="reload fixed top-0 left-10 w-4 h-4 m-4 p-2 transform transition-transform rotate-180 hover:rotate-360 active:rotate-540">
<svg fill="currentColor" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 489.645 489.645" xml:space="preserve" class="w-full h-full">
<g>
@ -196,8 +196,23 @@
{#each classes as className, i}
<div class:hover={hoveredPiece?.i == i} class:disabled={containerStates[i]} class:current={currentContainer === i} class:highlighted={highlightedContainer === i} class="squares-container {className}">
{#each (new Array(9)) as _, j}
{@const move = moves.find(move => move.i == i && move.j == j)}
<div on:click={() => addMove(i, j)} class:hover={hoveredPiece?.i == i && hoveredPiece.j == j} class="square" class:move class:preview={!move} class:cross={move && move.p==1} class:circle={move && move.p==2} on:mouseover={() => { if(currentContainer == i) hoveredPiece = { i, j } }} on:mouseleave={() => { if(hoveredPiece?.i == i && hoveredPiece.j == j) hoveredPiece = null; }}>
{@const moveIndex = moves.findIndex(cmove => cmove.i == i && cmove.j == j)}
{@const move = moves[moveIndex]}
<div
class:latest={moveIndex == moves.length - 1}
on:click={() => addMove(i, j)}
on:keydown={() => addMove(i, j)}
class:hover={hoveredPiece?.i == i && hoveredPiece.j == j}
class="square"
class:move
class:preview={!move}
class:cross={move && move.p==1}
class:circle={move && move.p==2}
on:mouseover={() => { if(currentContainer == i) hoveredPiece = { i, j } }}
on:focus={() => { if(currentContainer == i) hoveredPiece = { i, j }}}
on:mouseleave={() => { if(hoveredPiece?.i == i && hoveredPiece.j == j) hoveredPiece = null; }}
>
{#if move}
{#if move.p == 1}
<svg width="16" height="16">
@ -273,18 +288,18 @@
{/if}
{#key currentPlayer}
<div class="absolute top-200 left-0 right-0 text-center" in:fly={{ delay: 300, duration: 300, easing: quadOut, opacity: 0, y: 30 }} out:fly={{ delay: 0, duration: 300, easing: quadOut, opacity: 0, y: -30 }}>
{#if overallState == 1 || currentPlayer == 1}
<svg width="16" height="16" class="text-red-500">
<div class="absolute top-200 left-0 right-0 text-center flex items-center justify-center gap-2" in:fly={{ delay: 300, duration: 300, easing: quadOut, opacity: 0, y: 30 }} out:fly={{ delay: 0, duration: 300, easing: quadOut, opacity: 0, y: -30 }}>
{#if overallState == 1 || !overallState && currentPlayer == 1}
<svg width="16" height="16" class="mb-0.5 text-red-500">
<line x1="0" y1="0" x2="100%" y2="100%" stroke="currentColor" stroke-width="2" />
<line x1="100%" y1="0" x2="0" y2="100%" stroke="currentColor" stroke-width="2" />
</svg>
{:else if overallState == 2 || currentPlayer == 2}
<svg width="16" height="16" class="text-blue-500">
{:else if overallState == 2 || !overallState && currentPlayer == 2}
<svg width="16" height="16" class="mb-0.5 text-blue-500">
<circle cx="50%" cy="50%" r="45%" stroke="currentColor" stroke-width="2" fill="none" />
</svg>
{:else}
<svg width="16" height="16">
<svg width="16" height="16" class="mb-0.5">
<line x1="0" y1="0" x2="100%" y2="100%" stroke="currentColor" stroke-width="2" />
<line x1="100%" y1="0" x2="0" y2="100%" stroke="currentColor" stroke-width="2" />
<circle cx="50%" cy="50%" r="45%" stroke="currentColor" stroke-width="2" fill="none" />
@ -301,17 +316,17 @@
{:else}
is on turn.
{#if twoPlayer && self == currentPlayer}
<b>It is <span class:text-red-500={currentPlayer == 1} class:text-blue-500={currentPlayer == 2}>YOUR</span> {selfName ? "(" + selfName + ")" : ""} turn.</b>
<span class="-ml-1"><b>It is <span class:text-red-500={currentPlayer == 1} class:text-blue-500={currentPlayer == 2}>YOUR</span> {selfName ? "(" + selfName + ")" : ""} turn.</b></span>
{:else if twoPlayer && self != currentPlayer}
Waiting for <b class:text-red-500={currentPlayer == 1} class:text-blue-500={currentPlayer == 2}>{opponentName || "opponent"}</b>...
<span class="-ml-1">Waiting for <b class:text-red-500={currentPlayer == 1} class:text-blue-500={currentPlayer == 2}>{opponentName || "opponent"}</b>&hellip;</span>
{/if}
{/if}
</div>
{/key}
</div>
<div class="info min-w-38 px-4 h-100vh overflow-y-auto <md:w-full">
<div class="moves">
<div class="info min-w-38 px-4 h-full overflow-y-auto <md:w-full">
<div class="moves h-full">
{#if twoPlayer}
<div class="move text-red-500">
<svg width="16" height="16">
@ -339,7 +354,7 @@
<style>
.info .moves {
columns: 9.5rem auto;
@apply p-4 font-mono flex flex-col flex-wrap;
}
.move {
@apply p-1 flex gap-2 p-1 items-center leading-none;
@ -356,18 +371,19 @@
.winner-1 {
@apply text-red-500;
}
.moves {
@apply p-4 font-mono;
}
.board {
@apply grid grid-cols-3 grid-rows-3 gap-10 w-max h-max m-auto my-5;
@apply grid grid-cols-3 grid-rows-3 gap-10 w-max h-max m-auto my-5 flex-shrink-0;
}
.squares-container {
@apply grid grid-cols-3 grid-rows-3 gap-5 w-max h-max opacity-35 relative;
}
.square {
@apply border-black border-solid border w-6 h-6 p-4 cursor-pointer flex items-center justify-center;
@apply border-black border-solid border w-6 h-6 p-4 cursor-pointer flex items-center justify-center transition-none;
aspect-ratio: 1 / 1;
}
:global(.dark) .square {
@apply border-white;
}
.squares-container:not(.current) .square:active {
@apply bg-red-600/10;
@ -385,15 +401,18 @@
@apply cursor-not-allowed;
}
.square svg {
@apply w-full h-full;
@apply w-full h-full transition-none;
}
.square svg * {
@apply transition-none;
}
.square:hover, .square.hover {
@apply bg-black/5;
}
.square.hover.cross, .square:hover.cross {
.square.hover.cross, .square:hover.cross, .square.latest.cross {
@apply text-red-500;
}
.square.hover.circle, .square:hover.circle {
.square.hover.circle, .square:hover.circle, .square.latest.circle {
@apply text-blue-500;
}
.highlighted {

View file

@ -54,7 +54,7 @@
</div>
<main class="flex flex-col w-100vw h-100vh">
<div class="p-4 pl-16 flex justify-between border-b border-b-black border-b-solid">
<div class="p-4 pl-16 flex justify-between border-b border-b-black dark:border-b-white border-b-solid">
<h1 in:fly={{ delay: 0, duration, opacity: 0, y: 100, easing: quadOut }}>Rooms</h1>
<div class="right">
<button in:fly={{ delay: duration * 0.5, duration, opacity: 0, y: 100, easing: quadOut }} disabled>Quick match</button>
@ -100,14 +100,35 @@
@apply cursor-pointer border-b border-b-solid border-b-gray-300 p-4;
}
button {
@apply w-64 h-10 px-2 border border-gray-300 bg-white mt-4 my-0;
@apply w-64 h-10 px-2 border border-gray-300 bg-white mt-4 my-0 cursor-pointer;
}
button:disabled {
@apply bg-gray-300 cursor-not-allowed;
}
:global(.dark) button {
@apply bg-white/15 border-gray-700 text-white;
}
:global(.dark) button:hover {
@apply bg-white/10;
}
:global(.dark) button:disabled {
@apply bg-black;
}
:global(.dark) button:disabled:hover {
@apply bg-gray-900;
}
dialog .header button {
@apply w-12;
}
:global(.dark) dialog {
@apply bg-black border-gray-700 text-white;
}
input {
@apply w-64 h-10 px-2 border border-gray-300 rounded-l-lg mt-4 my-0;
}
:global(.dark) input {
@apply border-gray-400 text-white bg-black;
}
button:first-child {
@apply rounded-l-lg;
}

View file

@ -4,7 +4,8 @@
export var piece: number | "?";
export var latest: boolean = false;
</script>
<div class="move select-none" class:latest on:mouseover on:mouseout>
<div class="move select-none" class:latest on:mouseover on:mouseout on:focus on:blur>
<span class="player player-{player}">
{#if player == 1}
<svg width="16" height="16">
@ -46,6 +47,9 @@
.latest {
@apply opacity-35;
}
:global(.dark) .latest {
@apply opacity-50;
}
.player {
@apply inline-block w-1em h-1em;
}

View file

@ -18,7 +18,7 @@
}
</script>
<a href="/" class="arrow-back fixed top-0 left-0 w-4 h-4 m-4 p-2 transform transition-transform hover:-translate-x-1">
<a href="/" class="text-black dark:text-white arrow-back fixed top-0 left-0 w-4 h-4 m-4 p-2 transform transition-transform hover:-translate-x-1">
<svg width="16" height="16">
<line y1="50%" x1="0" y2="50%" x2="100%" stroke="currentColor" stroke-width="2" />
<line y1="50%" x1="0" y2="100%" x2="50%" stroke="currentColor" stroke-width="2" />
@ -43,9 +43,6 @@
{/if}
<style>
a {
@apply text-black;
}
* {
@apply box-border;
}
@ -61,8 +58,17 @@
input {
@apply w-64 h-10 px-2 border border-gray-300 rounded-l-lg mt-4 my-0;
}
:global(.dark) input {
@apply border-gray-400 text-white bg-black;
}
button {
@apply w-64 h-10 px-2 border border-gray-300 bg-white rounded-r-lg mt-4 my-0;
@apply w-64 h-10 px-2 border border-gray-300 bg-white rounded-r-lg mt-4 my-0 cursor-pointer;
}
:global(.dark) button {
@apply bg-white/5 border-gray-700 text-white;
}
:global(.dark) button:hover {
@apply bg-white/10;
}
form {
@apply h-50vh flex-grow-0;

View file

@ -0,0 +1,22 @@
import { writable } from 'svelte/store';
let theme = "light";
if(typeof window !== 'undefined') {
theme = localStorage.getItem('theme')!;
if(!theme && window.matchMedia('(prefers-color-scheme: dark)').matches) {
theme = "dark"
} else if(!theme) {
theme = "light"
}
}
export const themeStore = writable(theme);
let firstRun = true;
themeStore.subscribe(value => {
if(firstRun) {
firstRun = false;
return;
}
localStorage.setItem('theme', value);
});

View file

@ -6,7 +6,7 @@
const duration = 400;
</script>
<a on:click={() => { $connection?.leave() }} href="/multiplayer" class="arrow-back fixed top-0 left-0 w-4 h-4 m-4 p-2 transform transition-transform hover:-translate-x-1">
<a on:click={() => { $connection?.leave() }} href="/multiplayer" class="text-black dark:text-white arrow-back fixed top-0 left-0 w-4 h-4 m-4 p-2 transform transition-transform hover:-translate-x-1">
<svg width="16" height="16">
<line y1="50%" x1="0" y2="50%" x2="100%" stroke="currentColor" stroke-width="2" />
<line y1="50%" x1="0" y2="100%" x2="50%" stroke="currentColor" stroke-width="2" />
@ -16,7 +16,7 @@
<main>
<h1 in:fly={{ delay: 0, duration, opacity: 0, y: 100, easing: quadOut }}>Waiting for an opponent...</h1>
<p in:fly={{ delay: duration * 0.5, duration, opacity: 0, y: 100, easing: quadOut }}>Currently in room <code>{$room?.name}</code>.</p>
<p in:fly={{ delay: duration * 0.5, duration, opacity: 0, y: 100, easing: quadOut }}>Currently in room <code class="text-dark dark:text-white">{$room?.name}</code>.</p>
</main>
<style>
@ -26,10 +26,4 @@
p {
@apply text-gray-400;
}
a {
@apply text-black;
}
code {
@apply text-black;
}
</style>

View file

@ -1,13 +1,21 @@
<script lang="ts">
import PageTransition from "$lib/pageTransition.svelte";
import { themeStore } from "$lib/themeStore";
import "uno.css";
import type { load } from "./+layout";
export let data: Awaited<ReturnType<typeof load>>;
$: if(typeof window !== "undefined") {
if($themeStore == "dark") {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
}
</script>
<div class="overflow-hidden w-100vw">
<div class="overflow-hidden w-100vw h-100vh dark:bg-black dark:text-white">
<PageTransition url={data.url}>
<slot />
</PageTransition>
@ -17,4 +25,7 @@
:global(:root), :global(body) {
@apply overflow-hidden w-100vw;
}
:global(*) {
@apply transition-colors;
}
</style>

View file

@ -1,5 +1,7 @@
<script>
import { onMount } from "svelte";
import DarkmodeIcon from "$lib/DarkmodeIcon.svelte";
import { themeStore } from "$lib/themeStore";
import { onMount } from "svelte";
import { quadOut } from "svelte/easing";
import { fly } from "svelte/transition";
@ -10,6 +12,14 @@
onMount(() => {
shown = true;
});
function toggleDarkmode() {
if($themeStore == "dark") {
$themeStore = "light";
} else {
$themeStore = "dark";
}
}
</script>
<svelte:head>
@ -22,19 +32,19 @@
<a href="/localplay" class="single" in:fly={{ delay: 0, duration, opacity: 0, y: 100, easing: quadOut }}>
<h1>Local multiplayer</h1>
<img src="/computer.svg" alt="">
<div class="computer"></div>
<p>A game for two on a single device</p>
</a>
<a href="/multiplayer" class="multi" in:fly={{ delay: duration * 0.5, duration, opacity: 0, y: 100, easing: quadOut }}>
<h1>Online multiplayer</h1>
<img src="/network.svg" alt="">
<div class="multiplayer"></div>
<p>Play with 2 devices, even across the ocean.</p>
</a>
</div>
<div class="rules" in:fly={{ delay: 0, duration, opacity: 0, y: 100, easing: quadOut }}>
<div class="rules" in:fly={{ delay: duration, duration, opacity: 0, y: 100, easing: quadOut }}>
<div class="icon">
<svg fill="currentColor" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="800px" height="800px" viewBox="0 0 973.1 973.1" xml:space="preserve"
@ -56,10 +66,43 @@
<p>How do I play the game?</p>
</div>
</div>
<div class="darkmode">
<button on:click={toggleDarkmode} in:fly={{ delay: duration * 1.5, duration, opacity: 0, y: 100, easing: quadOut }}>
<DarkmodeIcon />
Toggle dark mode
</button>
</div>
</main>
{/if}
<style>
.computer {
@apply w-full bg-black;
aspect-ratio: 1/1;
mask: url('/computer.svg') no-repeat center;
-webkit-mask: url('/computer.svg') no-repeat center;
}
.multiplayer {
@apply w-full bg-black;
aspect-ratio: 1/1;
mask: url('/network.svg') no-repeat center;
-webkit-mask: url('/network.svg') no-repeat center;
}
:global(.dark) .computer, :global(.dark) .multiplayer {
@apply bg-white;
}
button {
@apply p-4 rounded-lg bg-transparent border-none cursor-pointer flex items-center justify-center gap-4;
}
button:hover {
@apply bg-black/10;
}
:global(.dark) button {
@apply text-white;
}
:global(.dark) button:hover {
@apply bg-white/10;
}
main {
@apply my-4 p-4 w-max m-auto h-100vh;
}
@ -69,9 +112,15 @@
.chooser > a {
@apply text-black no-underline cursor-pointer w-full p-8 border rounded-lg border-gray-400 border-solid;
}
:global(.dark) .chooser > a {
@apply text-white;
}
.chooser > a:hover {
@apply bg-black/10;
}
:global(.dark) .chooser > a:hover {
@apply bg-white/10;
}
.rules {
@apply cursor-not-allowed text-gray-500 flex justify-center items-center w-full my-8 p-4 border rounded-lg border-gray-400 border-solid;
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36c-0.98,1.37-2.58,2.26-4.4,2.26 c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>

After

Width:  |  Height:  |  Size: 380 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0 c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2 c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1 C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06 c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41 l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41 c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36 c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB