move components
This commit is contained in:
179
src/app/layout/Menu.svelte
Normal file
179
src/app/layout/Menu.svelte
Normal file
@ -0,0 +1,179 @@
|
||||
<script lang="ts">
|
||||
import MenuHome from '@assets/img/menu-home.webp';
|
||||
import MenuSignup from '@assets/img/menu-signup.webp';
|
||||
import MenuRules from '@assets/img/menu-rules.webp';
|
||||
import MenuFaq from '@assets/img/menu-faq.webp';
|
||||
import MenuFeedback from '@assets/img/menu-feedback.webp';
|
||||
import MenuTeam from '@assets/img/menu-team.webp';
|
||||
import MenuButton from '@assets/img/menu-button.webp';
|
||||
import MenuInventoryBar from '@assets/img/menu-inventory-bar.webp';
|
||||
import MenuSelectedFrame from '@assets/img/menu-selected-frame.webp';
|
||||
import { isBrowser } from '@antfu/utils';
|
||||
import { navigate } from 'astro:transitions/client';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let navPaths = $state([
|
||||
{
|
||||
name: 'Startseite',
|
||||
sprite: MenuHome.src,
|
||||
href: ``,
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: 'Registrieren',
|
||||
sprite: MenuSignup.src,
|
||||
href: 'signup',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: 'Regeln',
|
||||
sprite: MenuRules.src,
|
||||
href: 'rules',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: 'FAQ',
|
||||
sprite: MenuFaq.src,
|
||||
href: 'faq',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: 'Feedback & Kontakt',
|
||||
sprite: MenuFeedback.src,
|
||||
href: 'feedback',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: 'Team',
|
||||
sprite: MenuTeam.src,
|
||||
href: 'team',
|
||||
active: false
|
||||
}
|
||||
]);
|
||||
|
||||
let showMenuPermanent = $state(isBrowser ? localStorage.getItem('showMenuPermanent') === 'true' : false);
|
||||
let isTouch = $state(false);
|
||||
let isOpen = $state(false);
|
||||
let windowHeight = $state(0);
|
||||
|
||||
$effect(() => {
|
||||
localStorage.setItem('showMenuPermanent', `${showMenuPermanent}`);
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
new MutationObserver(() => {
|
||||
for (let i = 0; i < navPaths.length; i++) {
|
||||
console.log(navPaths[i].href, window.location.pathname);
|
||||
navPaths[i].active = new URL(navPaths[i].href).pathname === window.location.pathname;
|
||||
}
|
||||
}).observe(document.head, { childList: true });
|
||||
});
|
||||
|
||||
let navElem: HTMLDivElement;
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerHeight={windowHeight} />
|
||||
<svelte:body
|
||||
ontouchend={(e) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (isTouch && !navElem.contains(e.target)) showMenuPermanent = false;
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
class="fixed bottom-4 right-4 sm:left-4 sm:right-[initial] group/menu-bar flex flex-col-reverse justify-center items-center z-50 main-menu"
|
||||
bind:this={navElem}
|
||||
>
|
||||
<button
|
||||
class={isTouch ? 'btn btn-square relative w-16 h-16' : 'btn btn-square group/menu-button relative w-16 h-16'}
|
||||
onclick={() => {
|
||||
if (!isTouch) {
|
||||
let activePath = navPaths.find((path) => path.active);
|
||||
if (activePath !== undefined) {
|
||||
navigate(activePath.href);
|
||||
}
|
||||
showMenuPermanent = !showMenuPermanent;
|
||||
}
|
||||
}}
|
||||
ontouchend={() => {
|
||||
isTouch = true;
|
||||
showMenuPermanent = !showMenuPermanent;
|
||||
}}
|
||||
>
|
||||
<img class="absolute w-full h-full p-1 pixelated" src={MenuButton.src} alt="menu" />
|
||||
<img
|
||||
class="opacity-0 transition-opacity delay-50 group-hover/menu-button:opacity-100 absolute w-full h-full p-[3px] pixelated"
|
||||
class:opacity-100={isOpen || (isTouch && showMenuPermanent)}
|
||||
src={MenuSelectedFrame.src}
|
||||
alt="menu hover"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
class:hidden={!(isOpen || showMenuPermanent)}
|
||||
class={isTouch ? 'pb-3' : 'group-hover/menu-bar:block pb-3'}
|
||||
onmouseenter={() => (isOpen = true)}
|
||||
onmouseleave={() => (isOpen = false)}
|
||||
>
|
||||
<ul class="bg-base-200 rounded">
|
||||
{#each navPaths as navPath, i (navPath.href)}
|
||||
<li
|
||||
class="flex justify-center tooltip"
|
||||
class:tooltip-left={windowHeight > 450}
|
||||
class:sm:tooltip-right={windowHeight > 450}
|
||||
class:tooltip-top={windowHeight <= 450}
|
||||
class:tooltip-open={isTouch || windowHeight <= 450}
|
||||
data-tip={navPath.name}
|
||||
>
|
||||
<a
|
||||
class="btn btn-square border-none group/menu-item relative w-[3.5rem] h-[3.5rem] flex justify-center items-center"
|
||||
href={navPath.href}
|
||||
onclick={() => navigate(navPath.href)}
|
||||
>
|
||||
<div
|
||||
style="background-image: url({MenuInventoryBar.src}); background-position: -{i * 3.5}rem 0;"
|
||||
class="block w-full h-full bg-no-repeat bg-horizontal-sprite pixelated"
|
||||
></div>
|
||||
<div class="absolute flex justify-center items-center w-full h-full">
|
||||
<img class="w-1/2 h-1/2 pixelated" src={navPath.sprite} alt={navPath.name} />
|
||||
</div>
|
||||
<img
|
||||
class="transition-opacity delay-50 group-hover/menu-item:opacity-100 absolute w-full h-full pixelated scale-110 z-10"
|
||||
class:opacity-0={!navPath.active}
|
||||
src={MenuSelectedFrame.src}
|
||||
alt="menu hover"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@media (max-height: 450px) {
|
||||
.main-menu {
|
||||
flex-direction: row;
|
||||
}
|
||||
.main-menu > div {
|
||||
padding: 0.25rem 0 0 0.5rem;
|
||||
}
|
||||
.main-menu li {
|
||||
display: inline-block;
|
||||
|
||||
&::before {
|
||||
transform-origin: 0;
|
||||
transform: rotate(-90deg);
|
||||
margin-bottom: -0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pixelated {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.bg-horizontal-sprite {
|
||||
background-size: auto 100%;
|
||||
}
|
||||
</style>
|
69
src/app/website/index/Countdown.svelte
Normal file
69
src/app/website/index/Countdown.svelte
Normal file
@ -0,0 +1,69 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
let { start, end }: { start?: number; end: number } = $props();
|
||||
|
||||
let title = `Spielstart ist am ${new Date(import.meta.env.PUBLIC_START_DATE).toLocaleString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})} Uhr`;
|
||||
|
||||
function getUntil(): [number, number, number, number] {
|
||||
let diff = (end - (start || Date.now())) / 1000;
|
||||
|
||||
return [
|
||||
Math.floor(diff / (60 * 60 * 24)),
|
||||
Math.floor((diff % (60 * 60 * 24)) / (60 * 60)),
|
||||
Math.floor((diff % (60 * 60)) / 60),
|
||||
Math.floor(diff % 60)
|
||||
];
|
||||
}
|
||||
|
||||
let [days, hours, minutes, seconds] = $state(getUntil());
|
||||
let intervalId = setInterval(() => {
|
||||
[days, hours, minutes, seconds] = getUntil();
|
||||
if (start) start += 1000;
|
||||
}, 1000);
|
||||
|
||||
onDestroy(() => clearInterval(intervalId));
|
||||
</script>
|
||||
|
||||
<div
|
||||
class:hidden={days + hours + minutes + seconds < 0}
|
||||
class="grid grid-flow-col gap-5 text-center auto-cols-max text-white"
|
||||
>
|
||||
<div class="flex flex-col p-2 bg-gray-200/5 rounded-box backdrop-blur-sm" {title}>
|
||||
<span class="countdown font-mono text-3xl sm:text-6xl">
|
||||
<span class="m-auto" style="--value:{days};"></span>
|
||||
</span>
|
||||
Tage
|
||||
</div>
|
||||
<div class="flex flex-col p-2 bg-gray-200/5 rounded-box backdrop-blur-sm" {title}>
|
||||
<span class="countdown font-mono text-3xl sm:text-6xl">
|
||||
<span class="m-auto" style="--value:{hours};"></span>
|
||||
</span>
|
||||
Stunden
|
||||
</div>
|
||||
<div class="flex flex-col p-2 bg-gray-200/5 rounded-box backdrop-blur-sm" {title}>
|
||||
<span class="countdown font-mono text-3xl sm:text-6xl">
|
||||
<span class="m-auto" style="--value:{minutes};"></span>
|
||||
</span>
|
||||
Minuten
|
||||
</div>
|
||||
<div class="flex flex-col p-2 bg-gray-200/5 rounded-box backdrop-blur-sm" {title}>
|
||||
<span class="countdown font-mono text-3xl sm:text-6xl">
|
||||
<span class="m-auto" style="--value:{seconds};"></span>
|
||||
</span>
|
||||
Sekunden
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Set a custom content for the countdown before selector as it only supports numbers up to 99 */
|
||||
.countdown > ::before {
|
||||
content: '00\A 01\A 02\A 03\A 04\A 05\A 06\A 07\A 08\A 09\A 10\A 11\A 12\A 13\A 14\A 15\A 16\A 17\A 18\A 19\A 20\A 21\A 22\A 23\A 24\A 25\A 26\A 27\A 28\A 29\A 30\A 31\A 32\A 33\A 34\A 35\A 36\A 37\A 38\A 39\A 40\A 41\A 42\A 43\A 44\A 45\A 46\A 47\A 48\A 49\A 50\A 51\A 52\A 53\A 54\A 55\A 56\A 57\A 58\A 59\A 60\A 61\A 62\A 63\A 64\A 65\A 66\A 67\A 68\A 69\A 70\A 71\A 72\A 73\A 74\A 75\A 76\A 77\A 78\A 79\A 80\A 81\A 82\A 83\A 84\A 85\A 86\A 87\A 88\A 89\A 90\A 91\A 92\A 93\A 94\A 95\A 96\A 97\A 98\A 99\A 100\A 101\A 102\A 103\A 104\A 105\A 106\A 107\A 108\A 109\A 110\A 111\A 112\A 113\A 114\A 115\A 116\A 117\A 118\A 119\A 120\A 121\A 122\A 123\A 124\A 125\A 126\A 127\A 128\A 129\A 130\A 131\A 132\A 133\A 134\A 135\A 136\A 137\A 138\A 139\A 140\A 141\A 142\A 143\A 144\A 145\A 146\A 147\A 148\A 149\A 150\A 151\A 152\A 153\A 154\A 155\A 156\A 157\A 158\A 159\A 160\A 161\A 162\A 163\A 164\A 165\A 166\A 167\A 168\A 169\A 170\A 171\A 172\A 173\A 174\A 175\A 176\A 177\A 178\A 179\A 180\A 181\A 182\A 183\A 184\A 185\A 186\A 187\A 188\A 189\A 190\A 191\A 192\A 193\A 194\A 195\A 196\A 197\A 198\A 199\A 200\A 201\A 202\A 203\A 204\A 205\A 206\A 207\A 208\A 209\A 210\A 211\A 212\A 213\A 214\A 215\A 216\A 217\A 218\A 219\A 220\A 221\A 222\A 223\A 224\A 225\A 226\A 227\A 228\A 229\A 230\A 231\A 232\A 233\A 234\A 235\A 236\A 237\A 238\A 239\A 240\A 241\A 242\A 243\A 244\A 245\A 246\A 247\A 248\A 249\A 250\A 251\A 252\A 253\A 254\A 255\A 256\A 257\A 258\A 259\A 260\A 261\A 262\A 263\A 264\A 265\A 266\A 267\A 268\A 269\A 270\A 271\A 272\A 273\A 274\A 275\A 276\A 277\A 278\A 279\A 280\A 281\A 282\A 283\A 284\A 285\A 286\A 287\A 288\A 289\A 290\A 291\A 292\A 293\A 294\A 295\A 296\A 297\A 298\A 299\A 300\A 301\A 302\A 303\A 304\A 305\A 306\A 307\A 308\A 309\A 310\A 311\A 312\A 313\A 314\A 315\A 316\A 317\A 318\A 319\A 320\A 321\A 322\A 323\A 324\A 325\A 326\A 327\A 328\A 329\A 330\A 331\A 332\A 333\A 334\A 335\A 336\A 337\A 338\A 339\A 340\A 341\A 342\A 343\A 344\A 345\A 346\A 347\A 348\A 349\A 350\A 351\A 352\A 353\A 354\A 355\A 356\A 357\A 358\A 359\A 360\A 361\A 362\A 363\A 364\A';
|
||||
}
|
||||
</style>
|
39
src/app/website/index/Scroll.svelte
Normal file
39
src/app/website/index/Scroll.svelte
Normal file
@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
const { href } = $props();
|
||||
|
||||
let scrollY = $state(0);
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY />
|
||||
|
||||
<div class="flex items-center gap-x-2 transition-opacity duration-250" class:opacity-0={scrollY > 0}>
|
||||
<div class="divider divider-horizontal m-0"></div>
|
||||
<a {href} aria-label="scroll to teams">
|
||||
<div class="border-accent border-2 rounded-t-full rounded-b-full h-7 w-4 p-1">
|
||||
<div class="bg-accent rounded-full h-1 w-1 bounce"></div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="link text-sm" {href}>Zu den Teams</a>
|
||||
<div class="divider divider-horizontal m-0"></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@keyframes scrollDown {
|
||||
0% {
|
||||
transform: none;
|
||||
opacity: 0.25;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(250%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.bounce {
|
||||
animation: scrollDown 2s infinite;
|
||||
}
|
||||
</style>
|
@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Steve from '@assets/img/steve.png';
|
||||
import Team from '@components/website/Team.svelte';
|
||||
import type { GetDeathsRes } from '@db/schema/death.ts';
|
||||
import { type ActionReturnType, actions } from 'astro:actions';
|
||||
|
||||
@ -26,7 +25,10 @@
|
||||
{#each teams as team (team.id)}
|
||||
<tr>
|
||||
<td>
|
||||
<Team name={team.name} color={team.color} />
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="rounded-sm w-3 h-3" style="background-color: {team.color}"></div>
|
||||
<h3 class="text-xs sm:text-xl">{team.name}</h3>
|
||||
</div>
|
||||
</td>
|
||||
<td class="max-w-9 overflow-ellipsis">
|
||||
{#if team.memberOne.id}
|
102
src/app/website/signup/RegisteredPopup.svelte
Normal file
102
src/app/website/signup/RegisteredPopup.svelte
Normal file
@ -0,0 +1,102 @@
|
||||
<script lang="ts">
|
||||
import { registeredPopupState } from '@app/website/signup/RegisteredPopup.ts';
|
||||
import Input from '@components/input/Input.svelte';
|
||||
|
||||
interface Props {
|
||||
discordLink: string;
|
||||
paypalLink: string;
|
||||
teamspeakLink: string;
|
||||
startDate: string;
|
||||
}
|
||||
|
||||
let { discordLink, paypalLink, teamspeakLink, startDate }: Props = $props();
|
||||
|
||||
let skin: string | null = $state(null);
|
||||
|
||||
let modal: HTMLDialogElement;
|
||||
|
||||
registeredPopupState.subscribe(async (value) => {
|
||||
if (!value) return;
|
||||
|
||||
modal.show();
|
||||
|
||||
const skinview3d = await import('skinview3d');
|
||||
const skinViewer = new skinview3d.SkinViewer({
|
||||
width: 200,
|
||||
height: 300,
|
||||
renderPaused: true
|
||||
});
|
||||
|
||||
skinViewer.camera.rotation.x = -0.62;
|
||||
skinViewer.camera.rotation.y = 0.534;
|
||||
skinViewer.camera.rotation.z = 0.348;
|
||||
skinViewer.camera.position.x = 30.5;
|
||||
skinViewer.camera.position.y = 22.0;
|
||||
skinViewer.camera.position.z = 42.0;
|
||||
|
||||
await skinViewer.loadSkin(`https://mc-heads.net/skin/${value.username}`);
|
||||
skinViewer.render();
|
||||
skin = skinViewer.canvas.toDataURL();
|
||||
|
||||
skinViewer.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<dialog class="modal" bind:this={modal} onclose={($registeredPopupState = null)}>
|
||||
<form method="dialog" class="modal-box xl:w-5/12 max-w-10/12 z-10">
|
||||
<h1 class="text-center text-xl sm:text-3xl mb-8">Registrierung erfolgreich</h1>
|
||||
<p class="text-center font-bold">
|
||||
<span>Du hast Dich erfolgreich mit dem Team </span>
|
||||
<span class="inline-flex rounded-sm w-3 h-3" style="background-color: {$registeredPopupState?.teamColor}"></span>
|
||||
<span>{$registeredPopupState?.team}</span>
|
||||
<span> für Varo 4 registriert</span>. Spielstart ist am
|
||||
<i>
|
||||
{new Date(startDate).toLocaleString('de-DE', { day: '2-digit', month: 'long', year: 'numeric' })}
|
||||
</i>
|
||||
um
|
||||
<i>
|
||||
{new Date(startDate).toLocaleString('de-DE', { hour: '2-digit', minute: '2-digit' })} Uhr
|
||||
</i>.
|
||||
</p>
|
||||
<p class="text-center">Alle weiteren Informationen werden in der Whatsapp-Gruppe bekannt gegeben.</p>
|
||||
<p class="mt-2">
|
||||
Falls du uns unterstützen möchtest, kannst du dies ganz einfach über
|
||||
<a class="link" href={paypalLink} target="_blank">PayPal</a>
|
||||
tun. Antworten auf häufig gestellte Fragen findest du in unserer
|
||||
<a class="link" href="faq" target="_blank">FAQ</a>. Außerdem freuen wir uns, dich auf unserem
|
||||
<a class="link" href={teamspeakLink} target="_blank">TeamSpeak</a>
|
||||
oder in unserem
|
||||
<a class="link" href={discordLink} target="_blank">Discord</a>
|
||||
begrüßen zu dürfen!
|
||||
</p>
|
||||
<div class="divider"></div>
|
||||
<div class="flex justify-around mt-2 mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 w-full sm:w-fit gap-x-4 gap-y-2">
|
||||
<Input type="text" value={$registeredPopupState?.firstname} label="Vorname" disabled />
|
||||
<Input type="text" value={$registeredPopupState?.lastname} label="Nachname" disabled />
|
||||
<Input
|
||||
type="date"
|
||||
value={$registeredPopupState?.birthday.toISOString().substring(0, 10)}
|
||||
label="Geburtstag"
|
||||
size="sm"
|
||||
disabled
|
||||
/>
|
||||
<Input type="tel" value={$registeredPopupState?.phone} label="Telefonnummer" disabled />
|
||||
<Input type="text" value={$registeredPopupState?.username} label="Spielername" disabled />
|
||||
<Input type="text" value={$registeredPopupState?.teamMember} label="Mitspieler" disabled />
|
||||
</div>
|
||||
<div class="relative hidden md:flex justify-center w-[200px] my-4">
|
||||
{#if skin}
|
||||
<img class="absolute" src={skin} alt="" />
|
||||
{:else}
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="flex justify-center gap-8">
|
||||
<button class="btn">Weitere Person anmelden</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="absolute w-full h-full bg-black/50"></div>
|
||||
</dialog>
|
12
src/app/website/signup/RegisteredPopup.ts
Normal file
12
src/app/website/signup/RegisteredPopup.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export const registeredPopupState = atom<{
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
birthday: Date;
|
||||
phone: string;
|
||||
username: string;
|
||||
team: string;
|
||||
teamMember: string;
|
||||
teamColor: string;
|
||||
} | null>(null);
|
85
src/app/website/signup/RulesPopup.svelte
Normal file
85
src/app/website/signup/RulesPopup.svelte
Normal file
@ -0,0 +1,85 @@
|
||||
<script lang="ts">
|
||||
import { rulesPopupState, rulesPopupRead } from './RulesPopup.ts';
|
||||
import { rules } from '../../../rules.ts';
|
||||
|
||||
const modalTimeoutSeconds = 30;
|
||||
|
||||
let modalElem: HTMLDialogElement;
|
||||
|
||||
let modalTimer = $state(null);
|
||||
let modalSecondsOpen = $state(import.meta.env.PROD ? 0 : modalTimeoutSeconds);
|
||||
|
||||
rulesPopupState.listen((value) => {
|
||||
if (value == 'open') {
|
||||
modalElem.show();
|
||||
setInterval(() => modalSecondsOpen++, 1000);
|
||||
} else if (value == 'closed') {
|
||||
clearInterval(modalTimer!);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<dialog
|
||||
id="rules-popup"
|
||||
class="modal"
|
||||
onclose={() => {
|
||||
if ($rulesPopupState !== 'accepted') $rulesPopupState = 'closed';
|
||||
}}
|
||||
bind:this={modalElem}
|
||||
>
|
||||
<form method="dialog" class="modal-box flex flex-col max-h-[90%] max-w-[95%] md:max-w-[90%] lg:max-w-[75%]">
|
||||
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
||||
<div class="overflow-auto mt-5">
|
||||
<div class="mb-4">
|
||||
<div class="collapse collapse-arrow">
|
||||
<input type="checkbox" autocomplete="off" checked />
|
||||
<div class="collapse-title">
|
||||
<p>0. Vorwort</p>
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<p>{rules.header}</p>
|
||||
<p class="mt-1 text-[.75rem]">{rules.footer}</p>
|
||||
</div>
|
||||
<span class="block w-full h-[1px] mx-auto mb-1 bg-gray-600"></span>
|
||||
</div>
|
||||
{#each rules.sections as section, i (section.title)}
|
||||
<div class="collapse collapse-arrow">
|
||||
<input type="checkbox" autocomplete="off" />
|
||||
<div class="collapse-title">
|
||||
<p>{i + 1}. {section.title}</p>
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html section.content}
|
||||
</div>
|
||||
</div>
|
||||
<span class="block w-full h-[1px] mx-auto mb-1 bg-gray-600"></span>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="relative w-min"
|
||||
title={modalSecondsOpen < modalTimeoutSeconds
|
||||
? `Regeln können in ${Math.max(modalTimeoutSeconds - modalSecondsOpen, 0)} Sekunden akzeptiert werden`
|
||||
: ''}
|
||||
>
|
||||
<!--div class="absolute top-0 left-0 h-full w-full overflow-hidden rounded-lg">
|
||||
<div
|
||||
style="width: {Math.min((modalSecondsOpen / modalTimeoutSeconds) * 100, 100)}%"
|
||||
class="h-full bg-base-300"
|
||||
></div>
|
||||
</div-->
|
||||
<button
|
||||
class="btn btn-neutral"
|
||||
disabled={modalSecondsOpen < modalTimeoutSeconds}
|
||||
onclick={() => {
|
||||
$rulesPopupRead = true;
|
||||
$rulesPopupState = 'accepted';
|
||||
}}>Akzeptieren</button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]">
|
||||
<button class="!cursor-default">close</button>
|
||||
</form>
|
||||
</dialog>
|
4
src/app/website/signup/RulesPopup.ts
Normal file
4
src/app/website/signup/RulesPopup.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export const rulesPopupState = atom<'open' | 'closed' | 'accepted'>('closed');
|
||||
export const rulesPopupRead = atom(false);
|
30
src/app/website/signup/TeamPopup.svelte
Normal file
30
src/app/website/signup/TeamPopup.svelte
Normal file
@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { teamPopupOpen, teamPopupName } from '@app/website/signup/TeamPopup.ts';
|
||||
|
||||
let modal: HTMLDialogElement;
|
||||
let form: HTMLFormElement;
|
||||
|
||||
teamPopupOpen.subscribe((value) => {
|
||||
if (value) modal.show();
|
||||
else form?.reset();
|
||||
});
|
||||
</script>
|
||||
|
||||
<dialog class="modal" bind:this={modal} onclose={() => ($teamPopupOpen = false)}>
|
||||
<div class="modal-box">
|
||||
<form method="dialog">
|
||||
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
||||
</form>
|
||||
<form method="dialog" bind:this={form} onsubmit={() => ($teamPopupName = form.teamName.value)}>
|
||||
<h3 class="text-lg font-geist">Team erstellen</h3>
|
||||
<p class="py-4">Es wurde noch kein Team für dich und deinen Mitspieler erstellt.</p>
|
||||
<fieldset class="fieldset">
|
||||
<legend class="fieldset-legend">
|
||||
<span>Teamname <span class="text-red-700">*</span></span>
|
||||
</legend>
|
||||
<input id="teamName" name="teamName" class="input validator" type="text" required />
|
||||
</fieldset>
|
||||
<button class="mt-4 btn btn-neutral">Team registrieren</button>
|
||||
</form>
|
||||
</div>
|
||||
</dialog>
|
4
src/app/website/signup/TeamPopup.ts
Normal file
4
src/app/website/signup/TeamPopup.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export const teamPopupOpen = atom(false);
|
||||
export const teamPopupName = atom<string | null>(null);
|
Reference in New Issue
Block a user