website/src/routes/+layout.svelte
bytedream 9e282cf61b
All checks were successful
delpoy / build-and-deploy (push) Successful in 41s
update texts
2024-12-03 12:49:16 +01:00

259 lines
7.2 KiB
Svelte

<script lang="ts">
import '../app.css';
import { env } from '$env/dynamic/public';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import Input from '$lib/components/Input/Input.svelte';
import { setContext, tick } from 'svelte';
import type { PopupModalContextArgs } from '$lib/context';
let { children } = $props();
let navPaths = $state([
{
name: 'Startseite',
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-home.png`,
href: `${env.PUBLIC_BASE_PATH}/`,
active: false
},
{
name: 'Registrieren',
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-register.png`,
href: `${env.PUBLIC_BASE_PATH}/register`,
active: false
},
{
name: 'Regeln',
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-rules.png`,
href: `${env.PUBLIC_BASE_PATH}/rules`,
active: false
},
{
name: 'FAQ',
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-faq.png`,
href: `${env.PUBLIC_BASE_PATH}/faq`,
active: false
},
{
name: 'Feedback & Kontakt',
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-feedback.png`,
href: `${env.PUBLIC_BASE_PATH}/feedback`,
active: false
},
{
name: 'Team',
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-team.png`,
href: `${env.PUBLIC_BASE_PATH}/team`,
active: false
}
]);
let showMenuPermanent = $state(false);
let onAdminPage = $state(false);
let isTouch = $state(false);
let windowHeight = $state(0);
$effect(() => {
onAdminPage =
$page.url.pathname.startsWith(`${env.PUBLIC_BASE_PATH}/admin`) &&
$page.url.pathname !== `${env.PUBLIC_BASE_PATH}/admin/login`;
});
$effect(() => {
for (let i = 0; i < navPaths.length; i++) {
navPaths[i].active = navPaths[i].href === $page.url.pathname;
}
});
let popupModalState: PopupModalContextArgs | null = $state(null);
// eslint-disable-next-line no-undef
let popupModalNullTimeout: number | NodeJS.Timeout | null = null;
setContext('globalPopupModal', {
set: async ({ title, text, actions, onClose }: PopupModalContextArgs) => {
if (popupModalNullTimeout) clearTimeout(popupModalNullTimeout);
popupModalState = { title, text, actions, onClose };
await tick();
popupModalElem.showModal();
}
});
let navElem: HTMLDivElement;
let popupModalElem: HTMLDialogElement;
</script>
<svelte:window bind:innerHeight={windowHeight} />
<svelte:body
on:touchend={(e) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (isTouch && !navElem.contains(e.target)) showMenuPermanent = false;
}}
/>
<svelte:head>
{#if !onAdminPage}
<meta property="og:url" content={$page.url.toString()} />
<meta property="og:type" content="website" />
<meta property="og:image" content="{env.PUBLIC_BASE_PATH}/img/logo-512.png" />
{/if}
</svelte:head>
<main>
<div class="min-h-[calc(100vh-3.5rem)] h-full w-full" class:min-h-screen={onAdminPage}>
{@render children()}
</div>
</main>
<nav>
<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"
class:hidden={onAdminPage}
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) {
goto(activePath.href);
}
showMenuPermanent = !showMenuPermanent;
}
}}
ontouchend={() => {
isTouch = true;
showMenuPermanent = !showMenuPermanent;
}}
>
<img
class="absolute w-full h-full p-1 pixelated"
src="{env.PUBLIC_BASE_PATH}/img/menu-button.png"
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={isTouch && showMenuPermanent}
src="{env.PUBLIC_BASE_PATH}/img/selected-frame.png"
alt="menu hover"
/>
</button>
<div
class:hidden={!showMenuPermanent}
class={isTouch ? 'pb-3' : 'group-hover/menu-bar:block pb-3'}
>
<ul class="bg-base-200 rounded">
{#each navPaths as navPath, i}
<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={() => goto(navPath.href)}
>
<div
style="background-image: url('{env.PUBLIC_BASE_PATH}/img/menu-inventory-bar.png'); 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="{env.PUBLIC_BASE_PATH}/img/selected-frame.png"
alt="menu hover"
/>
</a>
</li>
{/each}
</ul>
</div>
</div>
</nav>
{#if !onAdminPage && $page.url.pathname !== `${env.PUBLIC_BASE_PATH}/register`}
<footer>
<div
class="flex justify-around items-center h-14 w-full bg-base-300"
class:hidden={onAdminPage}
>
<div class="hidden sm:block">
<p>© {new Date().getFullYear()} mhsl.eu</p>
</div>
<div class="flex gap-4">
<a class="link" href="https://mhsl.eu/id.html" target="_blank">Impressum</a>
<a class="link" href="https://mhsl.eu/datenschutz.html" target="_blank">Datenschutz</a>
</div>
</div>
</footer>
{/if}
<dialog class="modal" bind:this={popupModalElem}>
{#if popupModalState}
<form
method="dialog"
class="modal-box z-50"
onsubmit={() => {
popupModalNullTimeout = setTimeout(() => {
popupModalState = null;
popupModalNullTimeout = null;
}, 200);
popupModalState?.onClose?.();
}}
>
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button>
<h3 class="font-bold text-2xl">{popupModalState.title}</h3>
{#if popupModalState.text}
<p class="py-4 whitespace-pre-line">{popupModalState.text}</p>
{/if}
{#if popupModalState.actions}
<div class="flex flex-row space-x-1 mt-4">
{#each popupModalState.actions as action}
<Input type="submit" value={action.text} onclick={(e) => action.action?.(e)} />
{/each}
</div>
{/if}
</form>
<form
method="dialog"
class="modal-backdrop bg-[rgba(0,0,0,.2)]"
onsubmit={() => {
popupModalNullTimeout = setTimeout(() => {
popupModalState = null;
popupModalNullTimeout = null;
}, 200);
popupModalState?.onClose?.();
}}
>
<button>close</button>
</form>
{/if}
</dialog>
<style lang="scss">
@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;
}
}
}
</style>