vvandenb
2 years ago
18 changed files with 926 additions and 493 deletions
@ -1,76 +1,84 @@ |
|||
import svelte from 'rollup-plugin-svelte'; |
|||
import commonjs from '@rollup/plugin-commonjs'; |
|||
import resolve from '@rollup/plugin-node-resolve'; |
|||
import livereload from 'rollup-plugin-livereload'; |
|||
import { terser } from 'rollup-plugin-terser'; |
|||
import css from 'rollup-plugin-css-only'; |
|||
import svelte from "rollup-plugin-svelte"; |
|||
import commonjs from "@rollup/plugin-commonjs"; |
|||
import resolve from "@rollup/plugin-node-resolve"; |
|||
import livereload from "rollup-plugin-livereload"; |
|||
import { terser } from "rollup-plugin-terser"; |
|||
import css from "rollup-plugin-css-only"; |
|||
import autoPreprocess from "svelte-preprocess"; |
|||
import typescript from "@rollup/plugin-typescript"; |
|||
|
|||
const production = !process.env.ROLLUP_WATCH; |
|||
|
|||
function serve() { |
|||
let server; |
|||
let server; |
|||
|
|||
function toExit() { |
|||
if (server) server.kill(0); |
|||
} |
|||
function toExit() { |
|||
if (server) server.kill(0); |
|||
} |
|||
|
|||
return { |
|||
writeBundle() { |
|||
if (server) return; |
|||
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { |
|||
stdio: ['ignore', 'inherit', 'inherit'], |
|||
shell: true |
|||
}); |
|||
return { |
|||
writeBundle() { |
|||
if (server) return; |
|||
server = require("child_process").spawn( |
|||
"npm", |
|||
["run", "start", "--", "--dev"], |
|||
{ |
|||
stdio: ["ignore", "inherit", "inherit"], |
|||
shell: true, |
|||
} |
|||
); |
|||
|
|||
process.on('SIGTERM', toExit); |
|||
process.on('exit', toExit); |
|||
} |
|||
}; |
|||
process.on("SIGTERM", toExit); |
|||
process.on("exit", toExit); |
|||
}, |
|||
}; |
|||
} |
|||
|
|||
export default { |
|||
input: 'src/main.ts', |
|||
output: { |
|||
sourcemap: true, |
|||
format: 'iife', |
|||
name: 'app', |
|||
file: 'public/build/bundle.js' |
|||
}, |
|||
plugins: [ |
|||
svelte({ |
|||
compilerOptions: { |
|||
// enable run-time checks when not in production
|
|||
dev: !production |
|||
} |
|||
}), |
|||
// we'll extract any component CSS out into
|
|||
// a separate file - better for performance
|
|||
css({ output: 'bundle.css' }), |
|||
input: "src/main.ts", |
|||
output: { |
|||
sourcemap: true, |
|||
format: "iife", |
|||
name: "app", |
|||
file: "public/build/bundle.js", |
|||
}, |
|||
plugins: [ |
|||
svelte({ |
|||
preprocess: autoPreprocess(), |
|||
compilerOptions: { |
|||
// enable run-time checks when not in production
|
|||
dev: !production, |
|||
}, |
|||
}), |
|||
typescript({ sourceMap: !production }), |
|||
// we'll extract any component CSS out into
|
|||
// a separate file - better for performance
|
|||
css({ output: "bundle.css" }), |
|||
|
|||
// If you have external dependencies installed from
|
|||
// npm, you'll most likely need these plugins. In
|
|||
// some cases you'll need additional configuration -
|
|||
// consult the documentation for details:
|
|||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
|||
resolve({ |
|||
browser: true, |
|||
dedupe: ['svelte'] |
|||
}), |
|||
commonjs(), |
|||
// If you have external dependencies installed from
|
|||
// npm, you'll most likely need these plugins. In
|
|||
// some cases you'll need additional configuration -
|
|||
// consult the documentation for details:
|
|||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
|||
resolve({ |
|||
browser: true, |
|||
dedupe: ["svelte"], |
|||
}), |
|||
commonjs(), |
|||
|
|||
// In dev mode, call `npm run start` once
|
|||
// the bundle has been generated
|
|||
!production && serve(), |
|||
// In dev mode, call `npm run start` once
|
|||
// the bundle has been generated
|
|||
!production && serve(), |
|||
|
|||
// Watch the `public` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload('public'), |
|||
// Watch the `public` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload("public"), |
|||
|
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser() |
|||
], |
|||
watch: { |
|||
clearScreen: false |
|||
} |
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser(), |
|||
], |
|||
watch: { |
|||
clearScreen: false, |
|||
}, |
|||
}; |
|||
|
@ -1,76 +1,82 @@ |
|||
<script> |
|||
<script lang="ts"> |
|||
import Navbar from './components/NavBar.svelte'; |
|||
import Profile from './components/Profile.svelte'; |
|||
import MatchHistory from './components/MatchHistory.svelte'; |
|||
import type { Match } from './components/MatchHistory.svelte'; |
|||
import Friends from './components/Friends.svelte'; |
|||
import type { Friend } from './components/Friends.svelte'; |
|||
import Spectate from './components/Spectate.svelte'; |
|||
import type { SpectateType } from './components/Spectate.svelte'; |
|||
import Play from './components/Play.svelte'; |
|||
let isProfileOpen = false; |
|||
import Pong from './components/Pong/Pong.svelte'; |
|||
|
|||
let isProfileOpen = false; |
|||
function clickProfile() { |
|||
isProfileOpen = true; |
|||
isProfileOpen = true; |
|||
} |
|||
let isHistoryOpen = false; |
|||
function clickHistory() { |
|||
isHistoryOpen = true; |
|||
isHistoryOpen = true; |
|||
} |
|||
let matches = [ |
|||
{winner : "Alice", loser : "Bob", points : "-5", rank : "22"}, |
|||
{winner : "Alice", loser : "Bob", points : "10", rank : "24"}, |
|||
{winner : "Alice", loser : "Bob", points : "10", rank : "24"}, |
|||
{winner : "Alice", loser : "Bob", points : "7", rank : "23"}, |
|||
{winner : "Alice", loser : "Bob", points : "10", rank : "24"}, |
|||
{winner : "Alice", loser : "Bob", points : "10", rank : "24"}, |
|||
] |
|||
let matches: Array<Match> = [ |
|||
{ winner: 'Alice', loser: 'Bob', points: -5, rank: '22' }, |
|||
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }, |
|||
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }, |
|||
{ winner: 'Alice', loser: 'Bob', points: 7, rank: '23' }, |
|||
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }, |
|||
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' } |
|||
]; |
|||
let isFriendOpen = false; |
|||
function clickFriends() { |
|||
isFriendOpen = true; |
|||
isFriendOpen = true; |
|||
} |
|||
let friends = [ |
|||
{username : "Alice", status : "online"}, |
|||
{username : "Bob", status : "online"}, |
|||
{username : "Charlie", status : "offline"}, |
|||
{username : "Dave", status : "offline"}, |
|||
{username : "Eve", status : "in a game"}, |
|||
{username : "Frank", status : "online"}, |
|||
] |
|||
let friends: Array<Friend> = [ |
|||
{ username: 'Alice', status: 'online' }, |
|||
{ username: 'Bob', status: 'online' }, |
|||
{ username: 'Charlie', status: 'offline' }, |
|||
{ username: 'Dave', status: 'offline' }, |
|||
{ username: 'Eve', status: 'in a game' }, |
|||
{ username: 'Frank', status: 'online' } |
|||
]; |
|||
let isSpectateOpen = false; |
|||
function clickSpectate() { |
|||
isSpectateOpen = true; |
|||
isSpectateOpen = true; |
|||
} |
|||
let spectate = [ |
|||
{player1 : "Alice", player2 : "Bob", id : "1"}, |
|||
{player1 : "Alice", player2 : "Bob", id : "4"}, |
|||
{player1 : "Alice", player2 : "Bob", id : "6"}, |
|||
{player1 : "Alice", player2 : "Bob", id : "8"}, |
|||
{player1 : "Alice", player2 : "Bob", id : "2"}, |
|||
{player1 : "Alice", player2 : "Bob", id : "3"}, |
|||
] |
|||
let spectate: Array<SpectateType> = [ |
|||
{ player1: 'Alice', player2: 'Bob', id: '1' }, |
|||
{ player1: 'Alice', player2: 'Bob', id: '4' }, |
|||
{ player1: 'Alice', player2: 'Bob', id: '6' }, |
|||
{ player1: 'Alice', player2: 'Bob', id: '8' }, |
|||
{ player1: 'Alice', player2: 'Bob', id: '2' }, |
|||
{ player1: 'Alice', player2: 'Bob', id: '3' } |
|||
]; |
|||
</script> |
|||
|
|||
<main> |
|||
<Navbar clickProfile={clickProfile} clickHistory={clickHistory} clickFriends={clickFriends} clickSpectate={clickSpectate} /> |
|||
<Navbar {clickProfile} {clickHistory} {clickFriends} {clickSpectate} /> |
|||
{#if isSpectateOpen} |
|||
<div on:click={() => isSpectateOpen = false} on:keydown={() => isSpectateOpen = false}> |
|||
<Spectate spectate={spectate} /> |
|||
</div> |
|||
<div on:click={() => (isSpectateOpen = false)} on:keydown={() => (isSpectateOpen = false)}> |
|||
<Spectate {spectate} /> |
|||
</div> |
|||
{/if} |
|||
{#if isFriendOpen} |
|||
<div on:click={() => isFriendOpen = false} on:keydown={() => isFriendOpen = false}> |
|||
<Friends friends={friends} /> |
|||
</div> |
|||
<div on:click={() => (isFriendOpen = false)} on:keydown={() => (isFriendOpen = false)}> |
|||
<Friends {friends} /> |
|||
</div> |
|||
{/if} |
|||
{#if isHistoryOpen} |
|||
<div on:click={() => isHistoryOpen = false} on:keydown={() => isHistoryOpen = false}> |
|||
<MatchHistory matches={matches} /> |
|||
</div> |
|||
<div on:click={() => (isHistoryOpen = false)} on:keydown={() => (isHistoryOpen = false)}> |
|||
<MatchHistory {matches} /> |
|||
</div> |
|||
{/if} |
|||
{#if isProfileOpen} |
|||
<div on:click={() => isProfileOpen = false} on:keydown={() => isProfileOpen = false}> |
|||
<Profile username="Alice" wins={10} losses={5} elo={256} rank={23} is2faEnabled={false} /> |
|||
</div> |
|||
<div on:click={() => (isProfileOpen = false)} on:keydown={() => (isProfileOpen = false)}> |
|||
<Profile username="Alice" wins={10} losses={5} elo={256} rank={23} is2faEnabled={false} /> |
|||
</div> |
|||
{/if} |
|||
<Play /> |
|||
<Pong /> |
|||
</main> |
|||
|
|||
<style> |
|||
</style> |
|||
</style> |
|||
|
@ -1,69 +1,80 @@ |
|||
<script> |
|||
export let friends = []; |
|||
async function addFriend(event) { |
|||
event.preventDefault(); |
|||
const usernameInput = event.target.querySelector('input[type="text"]'); |
|||
const username = usernameInput.value; |
|||
// const response = await fetch('', { |
|||
// method: 'POST', |
|||
// headers: { |
|||
// 'Content-Type': 'application/json' |
|||
// }, |
|||
// body: JSON.stringify({ username }) |
|||
// }); |
|||
// if (response.ok) { |
|||
// console.log('Friend added successfully'); |
|||
// } else { |
|||
// console.log('Failed to add friend'); |
|||
// } |
|||
// usernameInput.value = ''; |
|||
alert('Trying to add friend' + username); |
|||
} |
|||
<script lang="ts" context="module"> |
|||
export interface Friend { |
|||
username: string; |
|||
status: 'online' | 'offline' | 'in a game'; |
|||
} |
|||
</script> |
|||
|
|||
<script lang="ts"> |
|||
export let friends: Array<Friend> = []; |
|||
async function addFriend(event: any) { |
|||
console.log(typeof event); |
|||
|
|||
event.preventDefault(); |
|||
const usernameInput = event.target.querySelector('input[type="text"]'); |
|||
console.log(usernameInput); |
|||
|
|||
const username = usernameInput.value; |
|||
// const response = await fetch('', { |
|||
// method: 'POST', |
|||
// headers: { |
|||
// 'Content-Type': 'application/json' |
|||
// }, |
|||
// body: JSON.stringify({ username }) |
|||
// }); |
|||
// if (response.ok) { |
|||
// console.log('Friend added successfully'); |
|||
// } else { |
|||
// console.log('Failed to add friend'); |
|||
// } |
|||
// usernameInput.value = ''; |
|||
alert('Trying to add friend' + username); |
|||
} |
|||
</script> |
|||
|
|||
<div class="overlay"> |
|||
<div class="friends" on:click|stopPropagation on:keydown|stopPropagation> |
|||
<div> |
|||
{#if friends.length > 0} |
|||
<h2>Monkey friends</h2> |
|||
{#each friends.slice(0, 10) as friends} |
|||
<li> |
|||
<span>{friends.username} is {friends.status}</span> |
|||
</li> |
|||
{/each} |
|||
{:else} |
|||
<p>No friends to display</p> |
|||
{/if} |
|||
<div> |
|||
<h3>Add a friend</h3> |
|||
<form on:submit={addFriend}> |
|||
<input type="text" /> |
|||
<button type="submit">Add</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="friends" on:click|stopPropagation on:keydown|stopPropagation> |
|||
<div> |
|||
{#if friends.length > 0} |
|||
<h2>Monkey friends</h2> |
|||
{#each friends.slice(0, 10) as friend} |
|||
<li> |
|||
<span>{friend.username} is {friend.status}</span> |
|||
</li> |
|||
{/each} |
|||
{:else} |
|||
<p>No friends to display</p> |
|||
{/if} |
|||
<div> |
|||
<h3>Add a friend</h3> |
|||
<form on:submit={addFriend}> |
|||
<input type="text" /> |
|||
<button type="submit">Add</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
z-index: 9998; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
z-index: 9998; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.friends { |
|||
background-color: #fff; |
|||
border: 1px solid #ccc; |
|||
border-radius: 5px; |
|||
padding: 1rem; |
|||
width: 300px; |
|||
} |
|||
</style> |
|||
.friends { |
|||
background-color: #fff; |
|||
border: 1px solid #ccc; |
|||
border-radius: 5px; |
|||
padding: 1rem; |
|||
width: 300px; |
|||
} |
|||
</style> |
|||
|
@ -1,49 +1,58 @@ |
|||
<script> |
|||
export let matches = []; |
|||
<script lang="ts" context="module"> |
|||
export interface Match { |
|||
winner: string; |
|||
loser: string; |
|||
points: number; |
|||
rank: string; |
|||
} |
|||
</script> |
|||
|
|||
<script lang="ts"> |
|||
export let matches: Array<Match> = []; |
|||
</script> |
|||
|
|||
<div class="overlay"> |
|||
<div class="history" on:click|stopPropagation on:keydown|stopPropagation> |
|||
<div> |
|||
{#if matches.length > 0} |
|||
<h2>Last 10 monkey games</h2> |
|||
{#each matches.slice(0, 10) as match} |
|||
<li> |
|||
<span>{match.winner} 1 - 0 {match.loser}</span> |
|||
{#if match.points > 0} |
|||
<span>+{match.points}</span> |
|||
{:else} |
|||
<span>{match.points}</span> |
|||
{/if} |
|||
<span>MP | rank #{match.rank}</span> |
|||
</li> |
|||
{/each} |
|||
{:else} |
|||
<p>No matches to display</p> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
<div class="history" on:click|stopPropagation on:keydown|stopPropagation> |
|||
<div> |
|||
{#if matches.length > 0} |
|||
<h2>Last 10 monkey games</h2> |
|||
{#each matches.slice(0, 10) as match} |
|||
<li> |
|||
<span>{match.winner} 1 - 0 {match.loser}</span> |
|||
{#if match.points > 0} |
|||
<span>+{match.points}</span> |
|||
{:else} |
|||
<span>{match.points}</span> |
|||
{/if} |
|||
<span>MP | rank #{match.rank}</span> |
|||
</li> |
|||
{/each} |
|||
{:else} |
|||
<p>No matches to display</p> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
z-index: 9998; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
z-index: 9998; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.history { |
|||
background-color: #fff; |
|||
border: 1px solid #ccc; |
|||
border-radius: 5px; |
|||
padding: 1rem; |
|||
width: 300px; |
|||
} |
|||
</style> |
|||
.history { |
|||
background-color: #fff; |
|||
border: 1px solid #ccc; |
|||
border-radius: 5px; |
|||
padding: 1rem; |
|||
width: 300px; |
|||
} |
|||
</style> |
|||
|
@ -1,106 +1,106 @@ |
|||
<script lang="ts"> |
|||
export let links = [ |
|||
{ text: "Home" , url : "img/pong.png" }, |
|||
{ text: "Spectate" }, |
|||
{ text: "Chat" }, |
|||
{ text: "History" }, |
|||
{ text: "Friends" }, |
|||
{ text: "Profile" } |
|||
]; |
|||
export let clickProfile = () => {}; |
|||
export let clickHistory = () => {}; |
|||
export let clickFriends = () => {}; |
|||
export let clickSpectate = () => {}; |
|||
export let links = [ |
|||
{ text: 'Home', url: 'img/pong.png' }, |
|||
{ text: 'Spectate' }, |
|||
{ text: 'Chat' }, |
|||
{ text: 'History' }, |
|||
{ text: 'Friends' }, |
|||
{ text: 'Profile' } |
|||
]; |
|||
export let clickProfile = () => {}; |
|||
export let clickHistory = () => {}; |
|||
export let clickFriends = () => {}; |
|||
export let clickSpectate = () => {}; |
|||
</script> |
|||
|
|||
<nav class="navigation-bar"> |
|||
<ul> |
|||
{#each links as link} |
|||
{#if link.text === "Spectate"} |
|||
<li> |
|||
<button on:click={clickSpectate}> |
|||
<p>Spectate</p> |
|||
</button> |
|||
</li> |
|||
{/if} |
|||
{#if link.text === "Friends"} |
|||
<li> |
|||
<button on:click={clickFriends}> |
|||
<p>Friends</p> |
|||
</button> |
|||
</li> |
|||
{/if} |
|||
{#if link.text === "Profile"} |
|||
<li> |
|||
<button on:click={clickProfile}> |
|||
<img src="img/profileicon.png" alt="profile icon"> |
|||
</button> |
|||
</li> |
|||
{/if} |
|||
{#if link.text === "History"} |
|||
<li> |
|||
<button on:click={clickHistory}> |
|||
<p>History</p> |
|||
</button> |
|||
</li> |
|||
{/if} |
|||
{#if link.text === "Home"} |
|||
<li> |
|||
<a href="/"> |
|||
<img src="img/pong.png" alt="home-icon"> |
|||
</a> |
|||
</li> |
|||
{/if} |
|||
{/each} |
|||
</ul> |
|||
<ul> |
|||
{#each links as link} |
|||
{#if link.text === 'Spectate'} |
|||
<li> |
|||
<button on:click={clickSpectate}> |
|||
<p>Spectate</p> |
|||
</button> |
|||
</li> |
|||
{/if} |
|||
{#if link.text === 'Friends'} |
|||
<li> |
|||
<button on:click={clickFriends}> |
|||
<p>Friends</p> |
|||
</button> |
|||
</li> |
|||
{/if} |
|||
{#if link.text === 'Profile'} |
|||
<li> |
|||
<button on:click={clickProfile}> |
|||
<img src="img/profileicon.png" alt="profile icon" /> |
|||
</button> |
|||
</li> |
|||
{/if} |
|||
{#if link.text === 'History'} |
|||
<li> |
|||
<button on:click={clickHistory}> |
|||
<p>History</p> |
|||
</button> |
|||
</li> |
|||
{/if} |
|||
{#if link.text === 'Home'} |
|||
<li> |
|||
<a href="/"> |
|||
<img src="img/pong.png" alt="home-icon" /> |
|||
</a> |
|||
</li> |
|||
{/if} |
|||
{/each} |
|||
</ul> |
|||
</nav> |
|||
|
|||
<style> |
|||
.navigation-bar { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background-color: #f5f5f5; |
|||
padding: 1rem; |
|||
} |
|||
.navigation-bar { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background-color: #f5f5f5; |
|||
padding: 1rem; |
|||
} |
|||
|
|||
.navigation-bar ul { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
list-style: none; |
|||
padding: 0; |
|||
} |
|||
.navigation-bar ul { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
list-style: none; |
|||
padding: 0; |
|||
} |
|||
|
|||
.navigation-bar li { |
|||
margin: 0 1rem; |
|||
} |
|||
.navigation-bar li { |
|||
margin: 0 1rem; |
|||
} |
|||
|
|||
.navigation-bar img { |
|||
width: 2rem; |
|||
height: 2rem; |
|||
} |
|||
.navigation-bar img { |
|||
width: 2rem; |
|||
height: 2rem; |
|||
} |
|||
|
|||
.navigation-bar button { |
|||
border: none; |
|||
} |
|||
.navigation-bar button { |
|||
border: none; |
|||
} |
|||
|
|||
@media (max-width: 768px) { |
|||
.navigation-bar { |
|||
flex-direction: column; |
|||
align-items: stretch; |
|||
} |
|||
@media (max-width: 768px) { |
|||
.navigation-bar { |
|||
flex-direction: column; |
|||
align-items: stretch; |
|||
} |
|||
|
|||
.navigation-bar ul { |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
.navigation-bar ul { |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.navigation-bar li { |
|||
margin: 0; |
|||
padding: 1rem; |
|||
text-align: center; |
|||
} |
|||
} |
|||
</style> |
|||
.navigation-bar li { |
|||
margin: 0; |
|||
padding: 1rem; |
|||
text-align: center; |
|||
} |
|||
} |
|||
</style> |
|||
|
@ -1,37 +1,38 @@ |
|||
<script> |
|||
// import { navigate } from 'svelte-routing'; |
|||
<script lang="ts"> |
|||
// import { navigate } from 'svelte-routing'; |
|||
|
|||
// function handleMatchmakingClick() { |
|||
// navigate('/matchmaking'); |
|||
// } |
|||
|
|||
// function handlePlayWithFriendClick() { |
|||
// navigate('/play-with-friend'); |
|||
// } |
|||
// function handleMatchmakingClick() { |
|||
// navigate('/matchmaking'); |
|||
// } |
|||
|
|||
// function handlePlayWithFriendClick() { |
|||
// navigate('/play-with-friend'); |
|||
// } |
|||
</script> |
|||
|
|||
<!-- dr --> |
|||
<main> |
|||
<h1>Choose a gamemode</h1> |
|||
<!-- <button on:click={handleMatchmakingClick}>Matchmaking</button> |
|||
<h1>Choose a gamemode</h1> |
|||
<!-- <button on:click={handleMatchmakingClick}>Matchmaking</button> |
|||
<button on:click={handlePlayWithFriendClick}>Play with a friend</button> --> |
|||
<button>Matchmaking</button> |
|||
<button>Play with a friend</button> |
|||
<button>Matchmaking</button> |
|||
<button>Play with a friend</button> |
|||
</main> |
|||
|
|||
<style> |
|||
main { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
} |
|||
main { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
} |
|||
|
|||
h1 { |
|||
margin-bottom: 2rem; |
|||
} |
|||
|
|||
h1 { |
|||
margin-bottom: 2rem; |
|||
} |
|||
|
|||
button { |
|||
font-size: 1.5rem; |
|||
padding: 1rem 2rem; |
|||
margin-bottom: 1rem; |
|||
} |
|||
</style> |
|||
button { |
|||
font-size: 1.5rem; |
|||
padding: 1rem 2rem; |
|||
margin-bottom: 1rem; |
|||
} |
|||
</style> |
|||
|
@ -0,0 +1,15 @@ |
|||
import { Point, Rect } from './utils'; |
|||
|
|||
export class Ball { |
|||
rect: Rect; |
|||
speed: Point; |
|||
color: string | CanvasGradient | CanvasPattern = 'white'; |
|||
|
|||
constructor(spawn: Point, size: Point = new Point(20, 20), speed: Point = new Point(10, 2)) { |
|||
this.rect = new Rect(spawn, size); |
|||
} |
|||
|
|||
draw(context: CanvasRenderingContext2D) { |
|||
this.rect.draw(context, this.color); |
|||
} |
|||
} |
@ -0,0 +1,80 @@ |
|||
import { Ball } from './Ball'; |
|||
import { GAME_EVENTS } from './constants'; |
|||
import type { GameInfo, GameUpdate } from './constants'; |
|||
import { Paddle } from './Paddle'; |
|||
import { Player } from './Player'; |
|||
import { formatWebsocketData, Point } from './utils'; |
|||
|
|||
const BG_COLOR = 'black'; |
|||
|
|||
export class Game { |
|||
canvas: HTMLCanvasElement; |
|||
context: CanvasRenderingContext2D; |
|||
ball: Ball; |
|||
players: Player[]; |
|||
my_paddle: Paddle; |
|||
|
|||
constructor(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D) { |
|||
this.canvas = canvas; |
|||
this.context = context; |
|||
this.players = []; |
|||
this.my_paddle = null; |
|||
} |
|||
|
|||
setInfo(data: GameInfo) { |
|||
this.canvas.width = data.mapSize.x; |
|||
this.canvas.height = data.mapSize.y; |
|||
|
|||
this.ball = new Ball(new Point(this.canvas.width / 2, this.canvas.height / 2), data.ballSize); |
|||
|
|||
const paddle1: Paddle = new Paddle(new Point(data.playerXOffset, this.canvas.height / 2), data.paddleSize); |
|||
const paddle2: Paddle = new Paddle( |
|||
new Point(this.canvas.width - data.playerXOffset, this.canvas.height / 2), |
|||
data.paddleSize |
|||
); |
|||
this.players = [new Player(paddle1), new Player(paddle2)]; |
|||
this.my_paddle = this.players[data.yourPaddleIndex].paddle; |
|||
} |
|||
|
|||
start(socket: WebSocket) { |
|||
if (this.my_paddle) { |
|||
this.canvas.addEventListener('mousemove', (e) => { |
|||
this.my_paddle.move(e); |
|||
socket.send( |
|||
formatWebsocketData(GAME_EVENTS.PLAYER_MOVE, { |
|||
position: this.my_paddle.rect.center |
|||
}) |
|||
); |
|||
}); |
|||
console.log('Game started!'); |
|||
} |
|||
} |
|||
|
|||
update(data: GameUpdate) { |
|||
if (this.players[0].paddle != this.my_paddle) { |
|||
this.players[0].paddle.rect.center = data.paddlesPositions[0]; |
|||
} |
|||
if (this.players[1].paddle != this.my_paddle) { |
|||
this.players[1].paddle.rect.center = data.paddlesPositions[1]; |
|||
} |
|||
this.ball.rect.center = data.ballPosition; |
|||
for (let i = 0; i < data.scores.length; i++) { |
|||
this.players[i].score = data.scores[i]; |
|||
} |
|||
} |
|||
|
|||
draw() { |
|||
this.context.fillStyle = BG_COLOR; |
|||
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); |
|||
|
|||
this.players.forEach((p) => p.draw(this.context)); |
|||
this.ball.draw(this.context); |
|||
|
|||
const max_width = 50; |
|||
this.context.font = '50px Arial'; |
|||
const text_width = this.context.measureText('0').width; |
|||
const text_offset = 50; |
|||
this.players[0].drawScore(this.canvas.width / 2 - (text_width + text_offset), max_width, this.context); |
|||
this.players[1].drawScore(this.canvas.width / 2 + text_offset, max_width, this.context); |
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
import { Point, Rect } from './utils'; |
|||
|
|||
export class Paddle { |
|||
rect: Rect; |
|||
color: string | CanvasGradient | CanvasPattern = 'white'; |
|||
|
|||
constructor(spawn: Point, size: Point = new Point(6, 100)) { |
|||
this.rect = new Rect(spawn, size); |
|||
} |
|||
|
|||
draw(context: CanvasRenderingContext2D) { |
|||
this.rect.draw(context, this.color); |
|||
} |
|||
|
|||
move(e: MouseEvent) { |
|||
const canvas = e.target as HTMLCanvasElement; |
|||
const rect = canvas.getBoundingClientRect(); |
|||
const new_y = ((e.clientY - rect.top) * canvas.height) / rect.height; |
|||
|
|||
const offset: number = this.rect.size.y / 2; |
|||
if (new_y - offset < 0) { |
|||
this.rect.center.y = offset; |
|||
} else if (new_y + offset > canvas.height) { |
|||
this.rect.center.y = canvas.height - offset; |
|||
} else { |
|||
this.rect.center.y = new_y; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,19 @@ |
|||
import type { Paddle } from './Paddle'; |
|||
|
|||
export class Player { |
|||
paddle: Paddle; |
|||
score: number; |
|||
|
|||
constructor(paddle: Paddle) { |
|||
this.paddle = paddle; |
|||
this.score = 0; |
|||
} |
|||
|
|||
draw(context: CanvasRenderingContext2D) { |
|||
this.paddle.draw(context); |
|||
} |
|||
|
|||
drawScore(score_position_x: number, max_width: number, context: CanvasRenderingContext2D) { |
|||
context.fillText(this.score.toString(), score_position_x, 50, max_width); |
|||
} |
|||
} |
@ -0,0 +1,65 @@ |
|||
<script lang="ts"> |
|||
import { GAME_EVENTS } from './constants'; |
|||
import { Game } from './Game'; |
|||
import { formatWebsocketData } from './utils'; |
|||
|
|||
const FPS = 144; |
|||
const SERVER_URL = 'ws://localhost:3001'; |
|||
|
|||
const socket: WebSocket = new WebSocket(SERVER_URL); |
|||
socket.onopen = () => { |
|||
console.log('Connected to game server!'); |
|||
socket.send(formatWebsocketData(GAME_EVENTS.GET_GAME_INFO)); |
|||
}; |
|||
let canvas: HTMLCanvasElement; |
|||
let context: CanvasRenderingContext2D; |
|||
|
|||
//Get canvas and its context |
|||
window.onload = () => { |
|||
canvas = document.getElementById('pong_canvas') as HTMLCanvasElement; |
|||
if (canvas) { |
|||
context = canvas.getContext('2d') as CanvasRenderingContext2D; |
|||
if (context) { |
|||
setupGame(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
function setupGame() { |
|||
const game = new Game(canvas, context); |
|||
|
|||
socket.onmessage = function (e) { |
|||
const event_json = JSON.parse(e.data); |
|||
const event = event_json.event; |
|||
const data = event_json.data; |
|||
|
|||
if (event == GAME_EVENTS.START_GAME) { |
|||
game.start(socket); |
|||
} else if (event == GAME_EVENTS.GAME_TICK) { |
|||
game.update(data); |
|||
} else if (event == GAME_EVENTS.GET_GAME_INFO) { |
|||
game.setInfo(data); |
|||
setInterval(() => { |
|||
game.draw(); |
|||
}, 1000 / FPS); |
|||
console.log('Game loaded!'); |
|||
} else { |
|||
console.log('Received unknown event from server:'); |
|||
console.log(event_json); |
|||
} |
|||
}; |
|||
} |
|||
</script> |
|||
|
|||
<div> |
|||
<button |
|||
on:click={() => { |
|||
socket.send(formatWebsocketData(GAME_EVENTS.START_GAME)); |
|||
}} |
|||
> |
|||
Start game |
|||
</button> |
|||
<br /> |
|||
<br /> |
|||
<canvas id="pong_canvas" /> |
|||
</div> |
@ -0,0 +1,32 @@ |
|||
import { Point } from './utils'; |
|||
|
|||
export const GAME_EVENTS = { |
|||
START_GAME: 'START_GAME', |
|||
GAME_TICK: 'GAME_TICK', |
|||
PLAYER_MOVE: 'PLAYER_MOVE', |
|||
GET_GAME_INFO: 'GET_GAME_INFO' |
|||
}; |
|||
|
|||
export interface GameInfo extends GameInfoConstants { |
|||
yourPaddleIndex: number; |
|||
} |
|||
export interface GameInfoConstants { |
|||
mapSize: Point; |
|||
paddleSize: Point; |
|||
playerXOffset: number; |
|||
ballSize: Point; |
|||
winScore: number; |
|||
} |
|||
export const gameInfoConstants: GameInfoConstants = { |
|||
mapSize: new Point(600, 400), |
|||
paddleSize: new Point(6, 50), |
|||
playerXOffset: 50, |
|||
ballSize: new Point(20, 20), |
|||
winScore: 2 |
|||
}; |
|||
|
|||
export interface GameUpdate { |
|||
paddlesPositions: Point[]; |
|||
ballPosition: Point; |
|||
scores: number[]; |
|||
} |
@ -0,0 +1,52 @@ |
|||
import { GAME_EVENTS } from './constants'; |
|||
import { Game } from './Game'; |
|||
import { formatWebsocketData, Point } from './utils'; |
|||
|
|||
const FPS = 144; |
|||
|
|||
const socket: WebSocket = new WebSocket('ws://localhost:3001'); |
|||
socket.onopen = () => { |
|||
console.log('Connected to game server!'); |
|||
socket.send(formatWebsocketData(GAME_EVENTS.GET_GAME_INFO)); |
|||
}; |
|||
let canvas: HTMLCanvasElement; |
|||
let context: CanvasRenderingContext2D; |
|||
|
|||
//Get canvas and its context
|
|||
window.onload = () => { |
|||
document.getElementById('start_game_button').addEventListener('click', () => { |
|||
socket.send(formatWebsocketData(GAME_EVENTS.START_GAME)); |
|||
}); |
|||
canvas = document.getElementById('pong_canvas') as HTMLCanvasElement; |
|||
if (canvas) { |
|||
context = canvas.getContext('2d') as CanvasRenderingContext2D; |
|||
if (context) { |
|||
setupGame(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
function setupGame() { |
|||
const game = new Game(canvas, context); |
|||
|
|||
socket.onmessage = function (e) { |
|||
const event_json = JSON.parse(e.data); |
|||
const event = event_json.event; |
|||
const data = event_json.data; |
|||
|
|||
if (event == GAME_EVENTS.START_GAME) { |
|||
game.start(socket); |
|||
} else if (event == GAME_EVENTS.GAME_TICK) { |
|||
game.update(data); |
|||
} else if (event == GAME_EVENTS.GET_GAME_INFO) { |
|||
game.setInfo(data); |
|||
setInterval(() => { |
|||
game.draw(); |
|||
}, 1000 / FPS); |
|||
console.log('Game loaded!'); |
|||
} else { |
|||
console.log('Received unknown event from server:'); |
|||
console.log(event_json); |
|||
} |
|||
}; |
|||
} |
@ -0,0 +1,88 @@ |
|||
export class Point { |
|||
x: number; |
|||
y: number; |
|||
|
|||
constructor(x: number, y: number) { |
|||
this.x = x; |
|||
this.y = y; |
|||
} |
|||
|
|||
//Returns a new point
|
|||
add(other: Point) { |
|||
return new Point(this.x + other.x, this.y + other.y); |
|||
} |
|||
|
|||
//Modifies `this` point
|
|||
add_inplace(other: Point) { |
|||
this.x += other.x; |
|||
this.y += other.y; |
|||
} |
|||
|
|||
clone(): Point { |
|||
return new Point(this.x, this.y); |
|||
} |
|||
} |
|||
|
|||
export class Rect { |
|||
center: Point; |
|||
size: Point; |
|||
|
|||
constructor(center: Point, size: Point) { |
|||
this.center = center; |
|||
this.size = size; |
|||
} |
|||
|
|||
draw(context: CanvasRenderingContext2D, color: string | CanvasGradient | CanvasPattern) { |
|||
const offset: Point = new Point(this.size.x / 2, this.size.y / 2); |
|||
|
|||
context.fillStyle = color; |
|||
context.fillRect(this.center.x - offset.x, this.center.y - offset.y, this.size.x, this.size.y); |
|||
} |
|||
|
|||
//True if `this` rect contains `other` rect in the x-axis
|
|||
contains_x(other: Rect): boolean { |
|||
const offset: number = this.size.x / 2; |
|||
const offset_other: number = other.size.x / 2; |
|||
|
|||
if ( |
|||
this.center.x - offset <= other.center.x - offset_other && |
|||
this.center.x + offset >= other.center.x + offset_other |
|||
) |
|||
return true; |
|||
return false; |
|||
} |
|||
|
|||
//True if `this` rect contains `other` rect in the y-axis
|
|||
contains_y(other: Rect): boolean { |
|||
const offset: number = this.size.y / 2; |
|||
const offset_other: number = other.size.y / 2; |
|||
|
|||
if ( |
|||
this.center.y - offset <= other.center.y - offset_other && |
|||
this.center.y + offset >= other.center.y + offset_other |
|||
) |
|||
return true; |
|||
return false; |
|||
} |
|||
|
|||
collides(other: Rect): boolean { |
|||
const offset: Point = new Point(this.size.x / 2, this.size.y / 2); |
|||
const offset_other: Point = new Point(other.size.x / 2, other.size.y / 2); |
|||
|
|||
if ( |
|||
this.center.x - offset.x < other.center.x + offset_other.x && |
|||
this.center.x + offset.x > other.center.x - offset_other.x && |
|||
this.center.y - offset.y < other.center.y + offset_other.y && |
|||
this.center.y + offset.y > other.center.y - offset_other.y |
|||
) |
|||
return true; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
export function formatWebsocketData(event: string, data?: any): string { |
|||
return JSON.stringify({ |
|||
event, |
|||
data |
|||
}); |
|||
} |
@ -1,109 +1,109 @@ |
|||
<script> |
|||
export let username = ''; |
|||
export let realname = ''; |
|||
export let wins = 0; |
|||
export let losses = 0; |
|||
export let elo = 0; |
|||
export let rank = -1; |
|||
export let is2faEnabled = false; |
|||
async function handleSubmit (event) { |
|||
event.preventDefault(); |
|||
// const response = await fetch('', { |
|||
// method: 'POST', |
|||
// headers: { |
|||
// 'Content-Type': 'application/json' |
|||
// }, |
|||
// body: JSON.stringify({ |
|||
// username |
|||
// }) |
|||
// }); |
|||
// if (response.ok) { |
|||
// console.log('username updated'); |
|||
// } |
|||
// else { |
|||
// console.log('username update failed'); |
|||
// } |
|||
alert('Trying to update username to ' + username); |
|||
} |
|||
async function handleAvatarUpload (event) { |
|||
event.preventDefault(); |
|||
alert('Trying to upload avatar'); |
|||
} |
|||
async function handle2fa (event) { |
|||
event.preventDefault(); |
|||
alert('Trying to ' + (is2faEnabled ? 'disable' : 'enable') + ' 2FA'); |
|||
} |
|||
<script lang="ts"> |
|||
export let username = ''; |
|||
export let realname = ''; |
|||
export let wins = 0; |
|||
export let losses = 0; |
|||
export let elo = 0; |
|||
export let rank = -1; |
|||
export let is2faEnabled = false; |
|||
async function handleSubmit(event: Event) { |
|||
event.preventDefault(); |
|||
// const response = await fetch('', { |
|||
// method: 'POST', |
|||
// headers: { |
|||
// 'Content-Type': 'application/json' |
|||
// }, |
|||
// body: JSON.stringify({ |
|||
// username |
|||
// }) |
|||
// }); |
|||
// if (response.ok) { |
|||
// console.log('username updated'); |
|||
// } |
|||
// else { |
|||
// console.log('username update failed'); |
|||
// } |
|||
alert('Trying to update username to ' + username); |
|||
} |
|||
async function handleAvatarUpload(event: Event) { |
|||
event.preventDefault(); |
|||
alert('Trying to upload avatar'); |
|||
} |
|||
async function handle2fa(event: Event) { |
|||
event.preventDefault(); |
|||
alert('Trying to ' + (is2faEnabled ? 'disable' : 'enable') + ' 2FA'); |
|||
} |
|||
</script> |
|||
|
|||
<div class="overlay"> |
|||
<div class="profile" on:click|stopPropagation on:keydown|stopPropagation> |
|||
<div class="profile-header"> |
|||
<img class="profile-img" src="img/profileicon.png" alt="Profile Icon"> |
|||
<h3>{realname}</h3> |
|||
<form on:submit={handleAvatarUpload}> |
|||
<button type="submit">Upload Avatar</button> |
|||
</form> |
|||
</div> |
|||
<div class="profile-body"> |
|||
<form on:submit={handleSubmit}> |
|||
<div class="username"> |
|||
<label for="username">Username</label> |
|||
<input type="text" id="username" bind:value={username} /> |
|||
</div> |
|||
<button type="submit">Submit</button> |
|||
</form> |
|||
<p>Wins: {wins}</p> |
|||
<p>Losses: {losses}</p> |
|||
<p>Winrate: {wins / (wins + losses) * 100}%</p> |
|||
<p>Elo : {elo}</p> |
|||
<p>Rank: {rank}</p> |
|||
<form class="two-factor-auth" on:submit={handle2fa}> |
|||
<button type="submit"> |
|||
{#if is2faEnabled} |
|||
Disable 2FA |
|||
{:else} |
|||
Enable 2FA |
|||
{/if} |
|||
</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
<div class="profile" on:click|stopPropagation on:keydown|stopPropagation> |
|||
<div class="profile-header"> |
|||
<img class="profile-img" src="img/profileicon.png" alt="Profile Icon" /> |
|||
<h3>{realname}</h3> |
|||
<form on:submit={handleAvatarUpload}> |
|||
<button type="submit">Upload Avatar</button> |
|||
</form> |
|||
</div> |
|||
<div class="profile-body"> |
|||
<form on:submit={handleSubmit}> |
|||
<div class="username"> |
|||
<label for="username">Username</label> |
|||
<input type="text" id="username" bind:value={username} /> |
|||
</div> |
|||
<button type="submit">Submit</button> |
|||
</form> |
|||
<p>Wins: {wins}</p> |
|||
<p>Losses: {losses}</p> |
|||
<p>Winrate: {(wins / (wins + losses)) * 100}%</p> |
|||
<p>Elo : {elo}</p> |
|||
<p>Rank: {rank}</p> |
|||
<form class="two-factor-auth" on:submit={handle2fa}> |
|||
<button type="submit"> |
|||
{#if is2faEnabled} |
|||
Disable 2FA |
|||
{:else} |
|||
Enable 2FA |
|||
{/if} |
|||
</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
z-index: 9998; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
z-index: 9998; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.profile { |
|||
background-color: #fff; |
|||
border: 1px solid #ccc; |
|||
border-radius: 5px; |
|||
padding: 1rem; |
|||
width: 300px; |
|||
} |
|||
.profile { |
|||
background-color: #fff; |
|||
border: 1px solid #ccc; |
|||
border-radius: 5px; |
|||
padding: 1rem; |
|||
width: 300px; |
|||
} |
|||
|
|||
.profile-header { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
.profile-header { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.profile-img { |
|||
width: 50px; |
|||
height: 50px; |
|||
margin-right: 1rem; |
|||
} |
|||
.profile-img { |
|||
width: 50px; |
|||
height: 50px; |
|||
margin-right: 1rem; |
|||
} |
|||
|
|||
.two-factor-auth { |
|||
margin-top: 1rem; |
|||
} |
|||
</style> |
|||
.two-factor-auth { |
|||
margin-top: 1rem; |
|||
} |
|||
</style> |
|||
|
@ -1,46 +1,53 @@ |
|||
<script> |
|||
export let spectate = []; |
|||
export let watch = () => {}; |
|||
<script lang="ts" context="module"> |
|||
export interface SpectateType { |
|||
id: string; |
|||
player1: string; |
|||
player2: string; |
|||
} |
|||
</script> |
|||
|
|||
<script lang="ts"> |
|||
export let spectate: Array<SpectateType> = []; |
|||
export let watch = () => {}; |
|||
</script> |
|||
|
|||
<div class="overlay"> |
|||
<div class="spectate" on:click|stopPropagation on:keydown|stopPropagation> |
|||
<div> |
|||
{#if spectate.length > 0} |
|||
<h2>Monkey spectating</h2> |
|||
{#each spectate.slice(0, 10) as spectate} |
|||
<li> |
|||
<span>{spectate.player1} VS {spectate.player2}</span> |
|||
<button on:click={() => watch(spectate.id)}>Spectate</button> |
|||
</li> |
|||
{/each} |
|||
{:else} |
|||
<p>No matches to spectate</p> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
<div class="spectate" on:click|stopPropagation on:keydown|stopPropagation> |
|||
<div> |
|||
{#if spectate.length > 0} |
|||
<h2>Monkey spectating</h2> |
|||
{#each spectate.slice(0, 10) as _spectate} |
|||
<li> |
|||
<span>{_spectate.player1} VS {_spectate.player2}</span> |
|||
<button on:click={() => watch(_spectate.id)}>Spectate</button> |
|||
</li> |
|||
{/each} |
|||
{:else} |
|||
<p>No matches to spectate</p> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
z-index: 9998; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.spectate { |
|||
background-color: #fff; |
|||
border: 1px solid #ccc; |
|||
border-radius: 5px; |
|||
padding: 1rem; |
|||
width: 300px; |
|||
} |
|||
<style> |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
z-index: 9998; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
</style> |
|||
.spectate { |
|||
background-color: #fff; |
|||
border: 1px solid #ccc; |
|||
border-radius: 5px; |
|||
padding: 1rem; |
|||
width: 300px; |
|||
} |
|||
</style> |
|||
|
@ -1,24 +1,32 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"target": "es6", |
|||
"module": "es6", |
|||
"strict": true, |
|||
"noImplicitAny": true, |
|||
"jsx": "preserve", |
|||
"importHelpers": true, |
|||
"moduleResolution": "node", |
|||
"esModuleInterop": true, |
|||
"allowSyntheticDefaultImports": true, |
|||
"sourceMap": true, |
|||
"baseUrl": ".", |
|||
"types": ["svelte"], |
|||
"experimentalDecorators": true, |
|||
"emitDecoratorMetadata": true, |
|||
"skipDefaultLibCheck": true, |
|||
"strictNullChecks": false, |
|||
"declaration": false, |
|||
"downlevelIteration": true |
|||
}, |
|||
"include": ["src/**/*.ts"], |
|||
"exclude": ["node_modules/*", "public/*"] |
|||
"compilerOptions": { |
|||
"target": "es6", |
|||
"module": "es6", |
|||
"strict": true, |
|||
"noImplicitAny": true, |
|||
"jsx": "preserve", |
|||
"importHelpers": true, |
|||
"moduleResolution": "node", |
|||
"esModuleInterop": true, |
|||
"allowSyntheticDefaultImports": true, |
|||
"sourceMap": true, |
|||
"baseUrl": ".", |
|||
"types": [ |
|||
"svelte" |
|||
], |
|||
"experimentalDecorators": true, |
|||
"emitDecoratorMetadata": true, |
|||
"skipDefaultLibCheck": true, |
|||
"strictNullChecks": false, |
|||
"declaration": false, |
|||
"downlevelIteration": true |
|||
}, |
|||
"include": [ |
|||
"src/**/*" |
|||
], |
|||
"exclude": [ |
|||
"node_modules/*", |
|||
"public/*" |
|||
], |
|||
"extends": "@tsconfig/svelte/tsconfig.json" |
|||
} |
Loading…
Reference in new issue