remove bedrock signup from web ui
deploy / build-and-deploy (push) Successful in 16s

This commit is contained in:
bytedream committed 2026-10-29 18:26:10 +01:00
1 parent b32fc64de4
commit 2b983e5c6d
10 files changed
+444 -271

No files matched your search

@@ -0,0 +1,22 @@
<script lang="ts">
interface Props {
serverIp: string;
}
let { serverIp }: Props = $props();
</script>
<div class="alert">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<span>
Die Anmeldung für Bedrock-Spieler ist über die Webseite nicht mehr möglich. Die Anmeldung erfolgt zukünftig direkt
über den Minecraft-Server unter <b>{serverIp}</b>.
</span>
</div>
@@ -0,0 +1,33 @@
<script lang="ts">
interface Props {
onSelect: (edition: 'java' | 'bedrock') => void;
selected?: 'java' | 'bedrock' | null;
}
let { onSelect, selected = null }: Props = $props();
</script>
<div>
<div class="divider">Minecraft-Version</div>
<p class="text-center mb-4">Bitte wähle zuerst deine Minecraft-Version aus.</p>
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-4 mb-6">
<button
type="button"
class="btn btn-outline h-auto py-4 flex-col gap-1"
class:btn-active={selected === 'java'}
onclick={() => onSelect('java')}
>
<span class="text-lg">Java (PC)</span>
<span class="text-xs font-normal">Anmeldung über die Webseite</span>
</button>
<button
type="button"
class="btn btn-outline h-auto py-4 flex-col gap-1"
class:btn-active={selected === 'bedrock'}
onclick={() => onSelect('bedrock')}
>
<span class="text-lg">Bedrock (Konsole und Handy)</span>
<span class="text-xs font-normal">Anmeldung über den Minecraft-Server</span>
</button>
</div>
</div>
+265
View File
@@ -0,0 +1,265 @@
<script lang="ts">
import { actions } from 'astro:actions';
import { onDestroy } from 'svelte';
import Checkbox from '@components/input/Checkbox.svelte';
import Input from '@components/input/Input.svelte';
import Popup from '@components/popup/Popup.svelte';
import { popupState } from '@components/popup/Popup.ts';
import RegisteredPopup, { type RegistrationData } from './RegisteredPopup.svelte';
import RulesPopup from './RulesPopup.svelte';
import { onMount } from 'svelte';
interface Props {
signupHash?: string;
serverIp: string;
discordLink: string;
paypalLink: string;
teamspeakLink: string;
startDate: string;
onFinished: () => void;
}
let { signupHash, serverIp, discordLink, paypalLink, teamspeakLink, startDate, onFinished }: Props = $props();
let rulesAccepted = $state(false);
let rulesOpen = $state(false);
let rulesRead = $state(false);
let submitting = $state(false);
let registration = $state<RegistrationData | null>(null);
let form: HTMLFormElement;
function formPopupOnReload(event: BeforeUnloadEvent) {
if (
form.firstname.value ||
form.lastname.value ||
form.birthday.value ||
form.phone.value ||
form.username.value ||
form.privacy.checked ||
form.logs.checked ||
form.rules.checked
) {
event.preventDefault();
event.returnValue = '';
}
}
let beforeUnloadAbortFn: (() => void) | null = null;
onMount(() => {
window.addEventListener('beforeunload', formPopupOnReload);
beforeUnloadAbortFn = () => window.removeEventListener('beforeunload', formPopupOnReload);
});
onDestroy(() => {
beforeUnloadAbortFn?.();
});
function resetForm() {
form.reset();
rulesAccepted = false;
}
async function submit(event: SubmitEvent) {
event.preventDefault();
if (submitting) return;
submitting = true;
const { error } = await actions.signup.signup({
firstname: form.firstname.value,
lastname: form.lastname.value,
birthday: form.birthday.value,
phone: form.phone.value,
username: form.username.value,
edition: 'java',
hash: signupHash
});
submitting = false;
if (error) {
popupState.set({
type: 'error',
title: 'Fehler',
message: error.message
});
return;
}
registration = {
firstname: form.firstname.value,
lastname: form.lastname.value,
birthday: form.birthday.value,
phone: form.phone.value,
username: form.username.value,
edition: 'Java'
};
resetForm();
}
</script>
<div class="alert">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<span>
Du kannst dich auch direkt in Minecraft unter <b>{serverIp}</b> anmelden.
</span>
</div>
<form
bind:this={form}
onsubmit={(e) => {
submit(e);
return false;
}}
autocomplete="off"
method="get"
>
<div class="divider">Persönliche Angaben</div>
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-x-4">
<Input
id="firstname"
type="text"
label="Vorname"
required
validation={{
pattern: '^\\p{L}{2,}',
hint: 'Bitte gib Deinen vollständigen Vornamen an, dieser muss mindestens aus 2 Zeichen bestehen.'
}}
dynamicWidth
/>
<Input
id="lastname"
type="text"
label="Nachname"
required
validation={{
pattern: '^\\p{L}{2,}',
hint: 'Bitte gib Deinen vollständigen Nachnamen an, dieser muss mindestens aus 2 Zeichen bestehen.'
}}
dynamicWidth
/>
<Input
id="birthday"
type="date"
label="Geburtstag"
required
validation={{
max: new Date(Date.now() - 1000 * 60 * 60 * 24 * 365 * 6).toLocaleDateString('sv-SE'),
hint: 'Bitte gib Deinen vollständigen Geburtstag und die korrekte Jahreszahl an. Du musst mindestens 6 Jahre alt sein.'
}}
dynamicWidth
>
{#snippet notice()}
<span>Die Angabe hat keine Auswirkungen auf das Spielgeschehen.</span>
{/snippet}
</Input>
<Input
id="phone"
type="tel"
label="Telefonnummer"
validation={{
pattern: '^[+\\(\\)\\s\\d]+$',
hint: 'Bitte gib Deine vollständige Telefonnummer an, diese darf keine ungültigen Zeichen enthalten'
}}
dynamicWidth
>
{#snippet notice()}
Diese nutzen wir, um Dich in der Whatsapp-Gruppe zuzuordnen und kontaktieren zu können.
<br />
<b>Die Angabe ist freiwillig, hilft den Administratoren jedoch sehr!</b>
{/snippet}
</Input>
</div>
<div class="divider">Spiel</div>
<div class="mx-2 grid grid-cols-1 gap-x-4">
<Input id="username" type="text" label="Minecraft-Spielername" required dynamicWidth>
{#snippet notice()}
<span class="text-[.65rem]">
Bitte gib deinen Minecraft-Java-Namen an. Dieser muss exakt mit deinem Ingame-Namen übereinstimmen.
</span>
{/snippet}
</Input>
</div>
<div class="divider"></div>
<div class="mx-2 grid gap-y-3 mb-6">
<Checkbox id="privacy" required>
{#snippet label()}
Ich bin mit der Speicherung meiner in der Anmeldung angegebenen, persönlichen Daten einverstanden. Siehe
<a class="link" href="https://mhsl.eu/id.html" target="_blank">Datenschutz</a>
{/snippet}
</Checkbox>
<Checkbox id="logs" required>
{#snippet label()}
Ich bin mit der Speicherung in Form von Logs aller meiner, beim Spielen anfallenden, persönlichen Daten durch
den Server einverstanden
{/snippet}
{#snippet notice()}
<span class="text-[.65rem]">
Dies betrifft jede Interaktion im Spiel und zugehörige Daten wie z.B. Chatnachrichten welche vom Minecraft
Client an den Server übermittelt werden
</span>
{/snippet}
</Checkbox>
<Checkbox
id="rules"
required
bind:checked={rulesAccepted}
onclick={(event) => {
if (!rulesRead) {
event.preventDefault();
rulesOpen = true;
}
}}
>
{#snippet label()}
Ich bin mit den
<button
class="link"
onclick={(event) => {
event.preventDefault();
rulesOpen = true;
}}
>
Regeln
</button>
einverstanden und achte sie
{/snippet}
</Checkbox>
</div>
<button type="submit" class="btn btn-neutral" class:loading-overlay={submitting} disabled={submitting}>
Anmeldung absenden
</button>
</form>
<RulesPopup
open={rulesOpen}
onAccept={() => {
rulesRead = true;
rulesAccepted = true;
}}
onClose={() => (rulesOpen = false)}
/>
<Popup />
<RegisteredPopup
{registration}
{discordLink}
{paypalLink}
{teamspeakLink}
{startDate}
newRegistrationAllowed={!signupHash}
onClose={() => {
registration = null;
onFinished();
}}
/>
+37 -18
View File
@@ -1,26 +1,43 @@
<script module lang="ts">
export type RegistrationData = {
firstname: string;
lastname: string;
birthday: string;
phone: string;
username: string;
edition: string;
};
</script>
<script lang="ts">
import { registeredPopupState } from '@app/website/signup/RegisteredPopup.ts';
import Input from '@components/input/Input.svelte';
import { onDestroy } from 'svelte';
interface Props {
registration: RegistrationData | null;
discordLink: string;
paypalLink: string;
teamspeakLink: string;
startDate: string;
newRegistrationAllowed: boolean;
onClose: () => void;
}
let { discordLink, paypalLink, teamspeakLink, startDate, newRegistrationAllowed }: Props = $props();
let { registration, discordLink, paypalLink, teamspeakLink, startDate, newRegistrationAllowed, onClose }: Props =
$props();
let skin: string | null = $state(null);
let modal: HTMLDialogElement;
const cancel = registeredPopupState.subscribe(async (value) => {
if (!value) return;
$effect(() => {
if (!registration) return;
modal.show();
if (!modal.open) modal.show();
void loadSkin(registration.username);
});
async function loadSkin(username: string) {
skin = null;
const skinview3d = await import('skinview3d');
const skinViewer = new skinview3d.SkinViewer({
@@ -36,17 +53,19 @@
skinViewer.camera.position.y = 22.0;
skinViewer.camera.position.z = 42.0;
await skinViewer.loadSkin(`https://mc-heads.net/skin/${value.username}`);
try {
await skinViewer.loadSkin(`https://mc-heads.net/skin/${username}`);
skinViewer.render();
skin = skinViewer.canvas.toDataURL();
} catch {
// keep the loading spinner if the skin cannot be loaded
} finally {
skinViewer.dispose();
});
onDestroy(cancel);
}
}
</script>
<dialog class="modal" bind:this={modal} onclose={() => ($registeredPopupState = null)}>
<dialog class="modal" bind:this={modal} onclose={() => onClose()}>
<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>
@@ -73,12 +92,12 @@
<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} label="Geburtstag" disabled />
<Input type="tel" value={$registeredPopupState?.phone} label="Telefonnummer" disabled />
<Input type="text" value={$registeredPopupState?.username} label="Spielername" disabled />
<Input type="text" value={$registeredPopupState?.edition} label="Edition" disabled />
<Input type="text" value={registration?.firstname} label="Vorname" disabled />
<Input type="text" value={registration?.lastname} label="Nachname" disabled />
<Input type="date" value={registration?.birthday} label="Geburtstag" disabled />
<Input type="tel" value={registration?.phone} label="Telefonnummer" disabled />
<Input type="text" value={registration?.username} label="Spielername" disabled />
<Input type="text" value={registration?.edition} label="Edition" disabled />
</div>
<div class="relative hidden md:flex justify-center w-[200px] my-4">
{#if skin}
-10
View File
@@ -1,10 +0,0 @@
import { atom } from 'nanostores';
export const registeredPopupState = atom<{
firstname: string;
lastname: string;
birthday: string;
phone: string;
username: string;
edition: string;
} | null>(null);
+24 -19
View File
@@ -1,35 +1,41 @@
<script lang="ts">
import { rulesPopupState, rulesPopupRead } from './RulesPopup.ts';
import { rules } from '../../../rules.ts';
import { popupState } from '@components/popup/Popup.ts';
import { onDestroy } from 'svelte';
import { rules } from '../../../rules.ts';
interface Props {
open: boolean;
onAccept: () => void;
onClose: () => void;
}
let { open, onAccept, onClose }: Props = $props();
const modalTimeoutSeconds = 30;
let modalElem: HTMLDialogElement;
let modalTimer = $state<ReturnType<typeof setInterval> | null>(null);
let modalTimer: ReturnType<typeof setInterval> | null = null;
let modalSecondsOpen = $state(import.meta.env.PROD ? 0 : modalTimeoutSeconds);
const cancel = rulesPopupState.subscribe((value) => {
if (value == 'open') {
modalElem.show();
$effect(() => {
if (open) {
if (!modalElem.open) modalElem.show();
if (modalTimer === null) {
modalTimer = setInterval(() => modalSecondsOpen++, 1000);
} else if (value == 'closed') {
clearInterval(modalTimer!);
}
} else if (modalTimer !== null) {
clearInterval(modalTimer);
modalTimer = null;
}
});
onDestroy(cancel);
onDestroy(() => {
if (modalTimer !== null) clearInterval(modalTimer);
});
</script>
<dialog
class="modal"
onclose={() => {
if ($rulesPopupState !== 'accepted') $rulesPopupState = 'closed';
}}
bind:this={modalElem}
>
<dialog class="modal" onclose={() => onClose()} 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">
@@ -86,8 +92,7 @@
};
return;
}
$rulesPopupRead = true;
$rulesPopupState = 'accepted';
onAccept();
}}>Akzeptieren</button
>
</div>
-4
View File
@@ -1,4 +0,0 @@
import { atom } from 'nanostores';
export const rulesPopupState = atom<'open' | 'closed' | 'accepted'>('closed');
export const rulesPopupRead = atom(false);
+9 -219
View File
@@ -1,12 +1,7 @@
---
import WebsiteLayout from '@layouts/website/WebsiteLayout.astro';
import Checkbox from '@components/input/Checkbox.svelte';
import Input from '@components/input/Input.svelte';
import Select from '@components/input/Select.svelte';
import RulesPopup from '@app/website/signup/RulesPopup.svelte';
import Popup from '@components/popup/Popup.svelte';
import RegisteredPopup from '@app/website/signup/RegisteredPopup.svelte';
import { DISCORD_LINK, PAYPAL_LINK, START_DATE, TEAMSPEAK_LINK } from 'astro:env/server';
import SignupFlow from '@app/website/signup/SignupFlow.svelte';
import { DISCORD_LINK, PAYPAL_LINK, SERVER_IP, START_DATE, TEAMSPEAK_LINK } from 'astro:env/server';
interface Props {
signupDisabled?: {
@@ -25,128 +20,18 @@ const { signupDisabled, signupHash } = Astro.props;
class:list={[signupDisabled ? 'max-h-screen overflow-hidden' : undefined]}
>
<div class="relative grid card w-11/12 xl:w-2/3 2xl:w-1/2 p-6 my-12 bg-base-100 shadow-lg h-min">
<h1 class="text-center text-3xl lg:text-5xl">Anmeldung</h1>
<form id="signup" data-signup-hash={signupHash}>
<div class="divider">Persönliche Angaben</div>
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-x-4">
<Input
id="firstname"
type="text"
label="Vorname"
required
validation={{
pattern: '^\\p{L}{2,}',
hint: 'Bitte gib Deinen vollständigen Vornamen an, dieser muss mindestens aus 2 Zeichen bestehen.'
}}
dynamicWidth
/>
<Input
id="lastname"
type="text"
label="Nachname"
required
validation={{
pattern: '^\\p{L}{2,}',
hint: 'Bitte gib Deinen vollständigen Nachnamen an, dieser muss mindestens aus 2 Zeichen bestehen.'
}}
dynamicWidth
/>
<Input
id="birthday"
type="date"
label="Geburtstag"
required
validation={{
max: new Date(Date.now() - 1000 * 60 * 60 * 24 * 365 * 6).toLocaleDateString('sv-SE'),
hint: 'Bitte gib Deinen vollständigen Geburtstag und die korrekte Jahreszahl an. Du musst mindestens 6 Jahre alt sein.'
}}
dynamicWidth
>
<span slot="notice">Die Angabe hat keine Auswirkungen auf das Spielgeschehen.</span>
</Input>
<Input
id="phone"
type="tel"
label="Telefonnummer"
validation={{
pattern: '^[+\\(\\)\\s\\d]+$',
hint: 'Bitte gib Deine vollständige Telefonnummer an, diese darf keine ungültigen Zeichen enthalten'
}}
dynamicWidth
>
<span slot="notice">
Diese nutzen wir, um Dich in der Whatsapp-Gruppe zuzuordnen und kontaktieren zu können.
<br />
<b>Die Angabe ist freiwillig, hilft den Administratoren jedoch sehr!</b>
</span>
</Input>
</div>
<div class="divider">Spiel</div>
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-x-4">
<Input id="username" type="text" label="Minecraft-Spielername" required dynamicWidth>
<span slot="notice" class="text-[.65rem]">
Spielern der Bedrock-Edition wird empfohlen, ihren Microsoft-Account Namen einzugeben. Der
Bedrock-Spielername kann nicht immer einem Minecraft Account zugeordnet werden.
</span>
</Input>
<Select
id="edition"
values={{ java: 'Java (PC)', bedrock: 'Bedrock (Konsole und Handys)' }}
label="Edition"
required
dynamicWidth
/>
</div>
<div class="divider"></div>
<div class="mx-2 grid gap-y-3 mb-6">
<Checkbox id="checkbox" required>
<span slot="label">
Ich bin mit der Speicherung meiner in der Anmeldung angegebenen, persönlichen Daten einverstanden. Siehe{' '}
<a class="link" href="https://mhsl.eu/id.html" target="_blank">
Datenschutz
</a>
</span>
</Checkbox>
<Checkbox id="logs" required>
<span slot="label">
Ich bin mit der Speicherung in Form von Logs aller meiner, beim Spielen anfallenden, persönlichen Daten
durch den Server einverstanden
</span>
<span slot="notice" class="text-[.65rem]">
Dies betrifft jede Interaktion im Spiel und zugehörige Daten wie z.B. Chatnachrichten welche vom Minecraft
Client an den Server übermittelt werden
</span>
</Checkbox>
<Checkbox id="rules" required>
<span slot="label">
Ich bin mit den{' '}
<a class="link" onclick="">
Regeln
</a>{' '}
einverstanden und achte sie
</span>
</Checkbox>
</div>
<button id="send-btn" class="btn btn-neutral">
Anmeldung absenden
</button>
</form>
</div>
</div>
</WebsiteLayout>
<RulesPopup client:idle />
<Popup client:idle />
<RegisteredPopup
client:idle
<SignupFlow
client:load
{signupHash}
serverIp={SERVER_IP}
discordLink={DISCORD_LINK}
paypalLink={PAYPAL_LINK}
teamspeakLink={TEAMSPEAK_LINK}
startDate={START_DATE}
newRegistrationAllowed={!signupHash}
/>
</div>
</div>
</WebsiteLayout>
{signupDisabled && (
<div class="absolute top-0 left-0 w-full h-full bg-black/50 backdrop-blur-sm z-10 rounded-xl flex justify-center items-center flex-col gap-2 pt-20 lg:pt-0 px-5 lg:px-10 2xl:px-28">
@@ -154,98 +39,3 @@ const { signupDisabled, signupHash } = Astro.props;
<h3 class="text-xl sm:text-2xl">{signupDisabled.subMessage}</h3>
</div>
)}
<script>
import { actions } from 'astro:actions';
import { popupState } from '@components/popup/Popup.ts';
import { rulesPopupState, rulesPopupRead } from '@app/website/signup/RulesPopup.ts';
import { registeredPopupState } from '@app/website/signup/RegisteredPopup.ts';
function setupClientValidation() {
const rulesCheckbox = document.getElementById('rules') as HTMLInputElement;
const rulesCheckboxRulesLink = rulesCheckbox.nextElementSibling!.querySelector('.link') as HTMLAnchorElement;
// add popup state subscriber to check when the accepted button is clicked
rulesPopupState.subscribe((value) => {
if (value == 'accepted') rulesCheckbox.checked = true;
});
// add click handler to open rules popup to rules checkbox
rulesCheckbox.addEventListener('click', (e) => {
if (!rulesPopupRead.get()) {
e.preventDefault();
rulesPopupState.set('open');
}
});
// add click handler to open rules popup when clicking the rules link in the rules checkbox label
rulesCheckboxRulesLink!.addEventListener('click', () => rulesPopupState.set('open'));
}
function setupForm() {
const form = document.getElementById('signup')! as HTMLFormElement;
const sendBtn = document.getElementById('send-btn')! as HTMLButtonElement;
// reset form on site (re-)load
form.reset();
const sendSignup = async () => {
const { error } = await actions.signup.signup({
firstname: form.firstname.value,
lastname: form.lastname.value,
birthday: form.birthday.value,
phone: form.phone.value,
username: form.username.value,
edition: form.edition.value,
hash: form.dataset.signupHash
});
if (error) {
if (error.code == 'CONFLICT' || error.code == 'FORBIDDEN') {
popupState.set({
type: 'error',
title: 'Fehler',
message: error.message
});
} else {
popupState.set({
type: 'error',
title: 'Fehler',
message: error.message
});
}
return;
}
registeredPopupState.set({
firstname: form.firstname.value,
lastname: form.lastname.value,
birthday: form.birthday.value,
phone: form.phone.value,
username: form.username.value,
edition: form.edition.value[0].toUpperCase() + form.edition.value.slice(1)
});
const cancel = registeredPopupState.subscribe((value) => {
if (value) return;
cancel();
form.reset();
});
};
form.addEventListener('submit', async (e) => {
e.preventDefault();
sendBtn.classList.add('loading-overlay');
await sendSignup();
sendBtn.classList.remove('loading-overlay');
});
}
const pathname = document.location.pathname;
document.addEventListener('astro:page-load', () => {
if (document.location.pathname !== pathname) return;
setupClientValidation();
setupForm();
});
</script>
+51
View File
@@ -0,0 +1,51 @@
<script lang="ts">
import { tick } from 'svelte';
import BedrockSignup from './BedrockSignup.svelte';
import EditionSelection from './EditionSelection.svelte';
import JavaSignup from './JavaSignup.svelte';
interface Props {
signupHash?: string;
serverIp: string;
discordLink: string;
paypalLink: string;
teamspeakLink: string;
startDate: string;
}
let { signupHash, serverIp, discordLink, paypalLink, teamspeakLink, startDate }: Props = $props();
type Step = 'java' | 'bedrock' | null;
let step = $state<Step>(null);
async function selectEdition(edition: 'java' | 'bedrock') {
step = edition;
if (edition === 'java') {
await tick();
document.getElementById('firstname')?.focus();
}
}
</script>
<div>
<h1 class="text-center text-3xl lg:text-5xl">Anmeldung</h1>
<EditionSelection onSelect={selectEdition} selected={step} />
<div class:hidden={step !== 'java'}>
<JavaSignup
{signupHash}
{serverIp}
{discordLink}
{paypalLink}
{teamspeakLink}
{startDate}
onFinished={() => (step = null)}
/>
</div>
<div class:hidden={step !== 'bedrock'}>
<BedrockSignup {serverIp} />
</div>
</div>
+3 -1
View File
@@ -9,12 +9,13 @@
hint: string;
};
disabled?: boolean;
onclick?: (event: MouseEvent) => void;
label?: Snippet | string;
notice?: Snippet;
}
let { id, checked = $bindable(), required, validation, disabled, label, notice }: Props = $props();
let { id, checked = $bindable(), required, validation, disabled, onclick, label, notice }: Props = $props();
</script>
<fieldset class="fieldset">
@@ -28,6 +29,7 @@
class:validator={required || validation}
required={required ? true : null}
disabled={disabled ? true : null}
{onclick}
/>
<span class="ml-1">
{#if typeof label === 'string'}