Rewrite page in Svelte 5 #49
3 changed files with 71 additions and 27 deletions
|
@ -1,24 +1,66 @@
|
||||||
<svelte:options runes={true} />
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import {OpenInNewWindow} from "radix-icons-svelte";
|
||||||
|
import {quintInOut} from "svelte/easing";
|
||||||
|
import { slide } from "svelte/transition";
|
||||||
|
|
||||||
let {service} = $props();
|
let {service} = $props();
|
||||||
|
|
||||||
|
let hover = $state({
|
||||||
|
title: false,
|
||||||
|
link: false,
|
||||||
|
ext: false
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col border-t-4 rounded-xl bg-black/55 border-maintenance p-4 gap-y-2 w-[30rem] h-48">
|
<div class="flex flex-col border-t-4 rounded-xl bg-black/55 border-maintenance p-4 gap-y-2 w-[30rem] h-48">
|
||||||
<div class="flex flex-row justify-between pb-4">
|
<div class="flex flex-row justify-between pb-4">
|
||||||
<div class="flex flex-row">
|
<div class="flex flex-row gap-1 items-center" on:mouseover={() => hover.title = true} on:mouseleave={() => hover.title = false}>
|
||||||
<h1>ICO</h1>
|
{#if service.icon}
|
||||||
<a href={service.href} class="text-accent font-bold hover:text-primary">{service.name}</a>
|
<img width="24px" class="h-6 w-6 cursor-pointer" src={service.icon}>
|
||||||
|
{:else}
|
||||||
|
{/if}
|
||||||
|
<a href={service.href} class="text-accent font-bold {hover.title ? 'text-primary': 'text-secondary'}">{service.name}</a>
|
||||||
|
{#if hover.title}
|
||||||
|
<div transition:slide={{ delay: 100, duration: 200, easing: quintInOut, axis: 'x' }} class="grid items-center">
|
||||||
|
<OpenInNewWindow color={ hover.title ? "hsl(var(--primary))" : "hsl(var(--secondary)"} class="self-center"/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="border-b-2 rounded-md text-sm text-center w-16 text-maintenance border-maintenance">Loading</h1>
|
<h1 class="border-b-2 rounded-md text-sm text-center w-16 text-maintenance border-maintenance">Loading</h1>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm text-center text-accent text-wrap">{service.desc}</p>
|
<p class="text-sm text-center text-accent text-wrap">{service.desc}</p>
|
||||||
<p class="text-sm text-center text-destructive">{service.warn}</p>
|
<p class="text-sm text-center font-bold text-destructive">{service.warn}</p>
|
||||||
<div class="flex flex-row justify-around mt-auto">
|
<div class="grid {service.extLink ? 'grid-cols-2' : 'grid-cols-1'} justify-items-center mt-auto">
|
||||||
<a class="text-sm border-x-2 rounded-md px-2 text-accent hover:border-primary hover:text-primary" href={service.href}>Open</a>
|
<a
|
||||||
|
class="flex flex-row text-sm border-x-2 rounded-md px-2 text-accent hover:border-primary hover:text-primary"
|
||||||
|
href={service.href}
|
||||||
|
on:mouseover={() => hover.link = true}
|
||||||
|
on:mouseleave={() => hover.link = false}
|
||||||
|
>
|
||||||
|
Open
|
||||||
|
{#if hover.link}
|
||||||
|
<div transition:slide={{ delay: 100, duration: 200, easing: quintInOut, axis: 'x' }} class="pl-1 pr-0 grid items-center">
|
||||||
|
<OpenInNewWindow color={ hover.link ? "hsl(var(--primary))" : "hsl(var(--secondary)"} class="self-center"/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
{#if service.extLink}
|
{#if service.extLink}
|
||||||
<a class="text-sm border-x-2 rounded-md px-2 text-accent hover:border-primary hover:text-primary" href={service.extLink}>Official Site</a>
|
<a
|
||||||
|
class="flex flex-row text-sm border-x-2 rounded-md px-2 text-accent hover:border-primary hover:text-primary"
|
||||||
|
href={service.extLink}
|
||||||
|
on:mouseover={() => hover.ext = true}
|
||||||
|
on:mouseleave={() => hover.ext = false}
|
||||||
|
>
|
||||||
|
Official Site
|
||||||
|
{#if hover.ext}
|
||||||
|
<div transition:slide={{ delay: 100, duration: 200, easing: quintInOut, axis: 'x' }} class="pl-1 pr-0 grid items-center">
|
||||||
|
<OpenInNewWindow color={ hover.ext ? "hsl(var(--primary))" : "hsl(var(--secondary)"} class="self-center"/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -2,7 +2,7 @@
|
||||||
"services": [
|
"services": [
|
||||||
{
|
{
|
||||||
"name": "Nextcloud",
|
"name": "Nextcloud",
|
||||||
"icon": "/icons/nextcloud-logo.svg",
|
"icon": "/assets/icons/nextcloud-logo.svg",
|
||||||
"href": "https://nextcloud.neshweb.net/",
|
"href": "https://nextcloud.neshweb.net/",
|
||||||
"desc": "Self-hosted Cloud Storage Service",
|
"desc": "Self-hosted Cloud Storage Service",
|
||||||
"warn": "Note: Registration requires approval",
|
"warn": "Note: Registration requires approval",
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Kavita",
|
"name": "Kavita",
|
||||||
"icon": "/icons/kavita-logo.svg",
|
"icon": "/assets/icons/kavita-logo.svg",
|
||||||
"href": "https://kavita.neshweb.net",
|
"href": "https://kavita.neshweb.net",
|
||||||
"desc": "Self-hosted Manga Library",
|
"desc": "Self-hosted Manga Library",
|
||||||
"warn": "Registration via Admin invite",
|
"warn": "Registration via Admin invite",
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Images",
|
"name": "Images",
|
||||||
"icon": "/icons/images-logo.svg",
|
"icon": "/assets/icons/images-logo.svg",
|
||||||
"href": "https://imgs.neshweb.net/",
|
"href": "https://imgs.neshweb.net/",
|
||||||
"desc": "Self-hosted Chevereto Image Service",
|
"desc": "Self-hosted Chevereto Image Service",
|
||||||
"warn": "",
|
"warn": "",
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Calibre Web",
|
"name": "Calibre Web",
|
||||||
"icon": "/icons/calibre-logo.ico",
|
"icon": "/assets/icons/calibre-logo.ico",
|
||||||
"href": "https://calibre.neshweb.net/",
|
"href": "https://calibre.neshweb.net/",
|
||||||
"desc": "Self-hosted Ebook Library Service",
|
"desc": "Self-hosted Ebook Library Service",
|
||||||
"warn": "Note: Registration only via Admin",
|
"warn": "Note: Registration only via Admin",
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "PeerTube",
|
"name": "PeerTube",
|
||||||
"icon": "/icons/peertube-logo.svg",
|
"icon": "/assets/icons/peertube-logo.svg",
|
||||||
"href": "https://tube.neshweb.net/",
|
"href": "https://tube.neshweb.net/",
|
||||||
"desc": "Self-hosted PeerTube Instance",
|
"desc": "Self-hosted PeerTube Instance",
|
||||||
"warn": "Note: Registration only via Admin",
|
"warn": "Note: Registration only via Admin",
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Mastodon",
|
"name": "Mastodon",
|
||||||
"icon": "/icons/mastodon-logo.svg",
|
"icon": "/assets/icons/mastodon-logo.svg",
|
||||||
"href": "https://mastodon.neshweb.net/",
|
"href": "https://mastodon.neshweb.net/",
|
||||||
"desc": "Self-hosted Mastodon Instance",
|
"desc": "Self-hosted Mastodon Instance",
|
||||||
"warn": "Note: Registration requires approval",
|
"warn": "Note: Registration requires approval",
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Vaultwarden",
|
"name": "Vaultwarden",
|
||||||
"icon": "/icons/vaultwarden-logo.svg",
|
"icon": "/assets/icons/vaultwarden-logo.svg",
|
||||||
"href": "https://vault.neshweb.net",
|
"href": "https://vault.neshweb.net",
|
||||||
"desc": "Self-hosted Password Manager",
|
"desc": "Self-hosted Password Manager",
|
||||||
"warn": "Note: Invite only",
|
"warn": "Note: Invite only",
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Jellyfin",
|
"name": "Jellyfin",
|
||||||
"icon": "/icons/jellyfin-logo.svg",
|
"icon": "/assets/icons/jellyfin-logo.svg",
|
||||||
"href": "https://jellyfin.neshweb.net/",
|
"href": "https://jellyfin.neshweb.net/",
|
||||||
"desc": "Open-Source, Self-Hosted Media Platform",
|
"desc": "Open-Source, Self-Hosted Media Platform",
|
||||||
"warn": "Note: Registration only via Admin",
|
"warn": "Note: Registration only via Admin",
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Navidrome",
|
"name": "Navidrome",
|
||||||
"icon": "/icons/navidrome-logo.png",
|
"icon": "/assets/icons/navidrome-logo.png",
|
||||||
"href": "https://navidrome.neshweb.net/",
|
"href": "https://navidrome.neshweb.net/",
|
||||||
"desc": "Open-Source, Self-Hosted Music Streaming Platform",
|
"desc": "Open-Source, Self-Hosted Music Streaming Platform",
|
||||||
"warn": "Note: Registration only via Admin",
|
"warn": "Note: Registration only via Admin",
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Gitlab",
|
"name": "Gitlab",
|
||||||
"icon": "/icons/gitlab-logo.svg",
|
"icon": "/assets/icons/gitlab-logo.svg",
|
||||||
"href": "https://gitlab.neshweb.net/",
|
"href": "https://gitlab.neshweb.net/",
|
||||||
"desc": "Self-hosted Git Service",
|
"desc": "Self-hosted Git Service",
|
||||||
"warn": "Note: Registration only via Admin",
|
"warn": "Note: Registration only via Admin",
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Forgejo",
|
"name": "Forgejo",
|
||||||
"icon": "/icons/forgejo-logo.svg",
|
"icon": "/assets/icons/forgejo-logo.svg",
|
||||||
"href": "https://forgejo.neshweb.net/",
|
"href": "https://forgejo.neshweb.net/",
|
||||||
"desc": "Self-hosted Git Service",
|
"desc": "Self-hosted Git Service",
|
||||||
"warn": "Note: Registration only via Admin",
|
"warn": "Note: Registration only via Admin",
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Portainer",
|
"name": "Portainer",
|
||||||
"icon": "/icons/portainer-logo.png",
|
"icon": "/assets/icons/portainer-logo.png",
|
||||||
"href": "https://portainer.neshweb.net/",
|
"href": "https://portainer.neshweb.net/",
|
||||||
"desc": "Docker Container Manager",
|
"desc": "Docker Container Manager",
|
||||||
"warn": "Note: Admin Only",
|
"warn": "Note: Admin Only",
|
||||||
|
@ -101,7 +101,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Nginx",
|
"name": "Nginx",
|
||||||
"icon": "/icons/npm-logo.png",
|
"icon": "/assets/icons/npm-logo.png",
|
||||||
"href": "https://nginx.neshweb.net/",
|
"href": "https://nginx.neshweb.net/",
|
||||||
"desc": "Web-based Nginx Proxy Manager",
|
"desc": "Web-based Nginx Proxy Manager",
|
||||||
"warn": "Note: Admin Only",
|
"warn": "Note: Admin Only",
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Proxmox",
|
"name": "Proxmox",
|
||||||
"icon": "/icons/proxmox-logo.png",
|
"icon": "/assets/icons/proxmox-logo.png",
|
||||||
"href": "https://proxmox.neshweb.net/",
|
"href": "https://proxmox.neshweb.net/",
|
||||||
"desc": "Hypervisor Webinterface",
|
"desc": "Hypervisor Webinterface",
|
||||||
"warn": "Note: Admin Only",
|
"warn": "Note: Admin Only",
|
||||||
|
@ -117,7 +117,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Dockge",
|
"name": "Dockge",
|
||||||
"icon": "/icons/dockge-logo.png",
|
"icon": "/assets/icons/dockge-logo.png",
|
||||||
"href": "https://dockge.neshweb.net/",
|
"href": "https://dockge.neshweb.net/",
|
||||||
"desc": "Docker Compose WebUI",
|
"desc": "Docker Compose WebUI",
|
||||||
"warn": "Note: Admin Only",
|
"warn": "Note: Admin Only",
|
||||||
|
@ -127,19 +127,19 @@
|
||||||
"games": {
|
"games": {
|
||||||
"minecraft": {
|
"minecraft": {
|
||||||
"name": "Minecraft",
|
"name": "Minecraft",
|
||||||
"icon": "/icons/minecraft-logo.png",
|
"icon": "/assets/icons/minecraft-logo.png",
|
||||||
"href": "https://minecraft.neshweb.net/",
|
"href": "https://minecraft.neshweb.net/",
|
||||||
"desc": "View all currently available Minecraft Servers and their mods"
|
"desc": "View all currently available Minecraft Servers and their mods"
|
||||||
},
|
},
|
||||||
"ready_or_not": {
|
"ready_or_not": {
|
||||||
"name": "Ready or Not",
|
"name": "Ready or Not",
|
||||||
"icon": "/icons/ron-logo.png",
|
"icon": "/assets/icons/ron-logo.png",
|
||||||
"href": "https://readyornot.neshweb.net/",
|
"href": "https://readyornot.neshweb.net/",
|
||||||
"desc": "Collection of Floor Plans for the Game 'Ready or Not'"
|
"desc": "Collection of Floor Plans for the Game 'Ready or Not'"
|
||||||
},
|
},
|
||||||
"zomboid": {
|
"zomboid": {
|
||||||
"name": "Zomboid",
|
"name": "Zomboid",
|
||||||
"icon": "/icons/zomboid-logo.png",
|
"icon": "/assets/icons/zomboid-logo.png",
|
||||||
"ip": "91.13.248.30",
|
"ip": "91.13.248.30",
|
||||||
"status": "Online"
|
"status": "Online"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import pages from '$lib/components/pages.json';
|
import pages from '$lib/components/pages.json';
|
||||||
import Card from "$lib/components/Card.svelte";
|
import Card from "$lib/components/Card.svelte";
|
||||||
|
|
||||||
const services = pages.services;
|
const services = $state(pages.services);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<title>Services</title>
|
<title>Services</title>
|
||||||
|
@ -10,6 +12,6 @@
|
||||||
|
|
||||||
<div class="flex flex-row flex-wrap justify-center gap-10 p-8 overflow-auto h-full">
|
<div class="flex flex-row flex-wrap justify-center gap-10 p-8 overflow-auto h-full">
|
||||||
{#each services as service}
|
{#each services as service}
|
||||||
<Card {service} />
|
<Card bind:service />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
Loading…
Reference in a new issue