added a basic realtime validation example

This commit is contained in:
Samuel Andert 2023-07-28 11:27:52 +02:00
parent 47d26f8c46
commit 9c08a2cf11
7 changed files with 214 additions and 22 deletions

View File

@ -20,6 +20,7 @@
"@skeletonlabs/skeleton": "^1.10.0",
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.20.4",
"@tailwindcss/forms": "^0.5.4",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.14",
@ -31,11 +32,13 @@
"prettier-plugin-svelte": "^2.10.1",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"sveltekit-superforms": "^1.5.0",
"tailwindcss": "^3.3.3",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.4.2",
"vitest": "^0.32.2"
"vitest": "^0.32.2",
"zod": "^3.21.4"
},
"type": "module",
"dependencies": {

View File

@ -30,10 +30,10 @@ dependencies:
version: 2.4.3(svelte@4.1.1)(vite@4.4.7)
'@wagmi/core':
specifier: ^1.3.8
version: 1.3.8(react@18.2.0)(typescript@5.1.6)(viem@1.4.1)
version: 1.3.8(react@18.2.0)(typescript@5.1.6)(viem@1.4.1)(zod@3.21.4)
viem:
specifier: ^1.3.0
version: 1.4.1(typescript@5.1.6)
version: 1.4.1(typescript@5.1.6)(zod@3.21.4)
devDependencies:
'@iconify/svelte':
@ -51,6 +51,9 @@ devDependencies:
'@sveltejs/kit':
specifier: ^1.20.4
version: 1.22.3(svelte@4.1.1)(vite@4.4.7)
'@tailwindcss/forms':
specifier: ^0.5.4
version: 0.5.4(tailwindcss@3.3.3)
'@typescript-eslint/eslint-plugin':
specifier: ^5.45.0
version: 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.45.0)(typescript@5.1.6)
@ -84,6 +87,9 @@ devDependencies:
svelte-check:
specifier: ^3.4.3
version: 3.4.6(postcss@8.4.27)(svelte@4.1.1)
sveltekit-superforms:
specifier: ^1.5.0
version: 1.5.0(@sveltejs/kit@1.22.3)(svelte@4.1.1)(zod@3.21.4)
tailwindcss:
specifier: ^3.3.3
version: 3.3.3
@ -99,6 +105,9 @@ devDependencies:
vitest:
specifier: ^0.32.2
version: 0.32.4
zod:
specifier: ^3.21.4
version: 3.21.4
packages:
@ -1845,10 +1854,10 @@ packages:
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
dev: false
/@safe-global/safe-apps-provider@0.17.1(typescript@5.1.6):
/@safe-global/safe-apps-provider@0.17.1(typescript@5.1.6)(zod@3.21.4):
resolution: {integrity: sha512-lYfRqrbbK1aKU1/UGkYWc/X7PgySYcumXKc5FB2uuwAs2Ghj8uETuW5BrwPqyjBknRxutFbTv+gth/JzjxAhdQ==}
dependencies:
'@safe-global/safe-apps-sdk': 8.0.0(typescript@5.1.6)
'@safe-global/safe-apps-sdk': 8.0.0(typescript@5.1.6)(zod@3.21.4)
events: 3.3.0
transitivePeerDependencies:
- bufferutil
@ -1858,11 +1867,11 @@ packages:
- zod
dev: false
/@safe-global/safe-apps-sdk@8.0.0(typescript@5.1.6):
/@safe-global/safe-apps-sdk@8.0.0(typescript@5.1.6)(zod@3.21.4):
resolution: {integrity: sha512-gYw0ki/EAuV1oSyMxpqandHjnthZjYYy+YWpTAzf8BqfXM3ItcZLpjxfg+3+mXW8HIO+3jw6T9iiqEXsqHaMMw==}
dependencies:
'@safe-global/safe-gateway-typescript-sdk': 3.7.3
viem: 1.4.1(typescript@5.1.6)
viem: 1.4.1(typescript@5.1.6)(zod@3.21.4)
transitivePeerDependencies:
- bufferutil
- encoding
@ -2184,6 +2193,15 @@ packages:
transitivePeerDependencies:
- supports-color
/@tailwindcss/forms@0.5.4(tailwindcss@3.3.3):
resolution: {integrity: sha512-YAm12D3R7/9Mh4jFbYSMnsd6jG++8KxogWgqs7hbdo/86aWjjlIEvL7+QYdVELmAI0InXTpZqFIg5e7aDVWI2Q==}
peerDependencies:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
dependencies:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.3.3
dev: true
/@types/chai-subset@1.3.3:
resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
dependencies:
@ -2433,7 +2451,7 @@ packages:
typescript: 5.1.6
dev: false
/@wagmi/connectors@2.6.6(@wagmi/chains@1.6.0)(react@18.2.0)(typescript@5.1.6)(viem@1.4.1):
/@wagmi/connectors@2.6.6(@wagmi/chains@1.6.0)(react@18.2.0)(typescript@5.1.6)(viem@1.4.1)(zod@3.21.4):
resolution: {integrity: sha512-/o1c/TCivQs8DOAUOcQvY2UIt3p2mWOAHi39D0LC74+ncpXzLC5/gyaWU38qnTxPM8s/PmTmaWDgz+VhICXrag==}
peerDependencies:
'@wagmi/chains': '>=1.3.0'
@ -2447,17 +2465,17 @@ packages:
dependencies:
'@coinbase/wallet-sdk': 3.7.1
'@ledgerhq/connect-kit-loader': 1.1.0
'@safe-global/safe-apps-provider': 0.17.1(typescript@5.1.6)
'@safe-global/safe-apps-sdk': 8.0.0(typescript@5.1.6)
'@safe-global/safe-apps-provider': 0.17.1(typescript@5.1.6)(zod@3.21.4)
'@safe-global/safe-apps-sdk': 8.0.0(typescript@5.1.6)(zod@3.21.4)
'@wagmi/chains': 1.6.0(typescript@5.1.6)
'@walletconnect/ethereum-provider': 2.9.0(@walletconnect/modal@2.5.9)
'@walletconnect/legacy-provider': 2.0.0
'@walletconnect/modal': 2.5.9(react@18.2.0)
'@walletconnect/utils': 2.9.0
abitype: 0.8.7(typescript@5.1.6)
abitype: 0.8.7(typescript@5.1.6)(zod@3.21.4)
eventemitter3: 4.0.7
typescript: 5.1.6
viem: 1.4.1(typescript@5.1.6)
viem: 1.4.1(typescript@5.1.6)(zod@3.21.4)
transitivePeerDependencies:
- '@react-native-async-storage/async-storage'
- bufferutil
@ -2469,7 +2487,7 @@ packages:
- zod
dev: false
/@wagmi/core@1.3.8(react@18.2.0)(typescript@5.1.6)(viem@1.4.1):
/@wagmi/core@1.3.8(react@18.2.0)(typescript@5.1.6)(viem@1.4.1)(zod@3.21.4):
resolution: {integrity: sha512-OYSxikoMizqVnpSkFTwGE7PwFaz2k0PXteSiI0W2Mtk4j4sZzRFdP+9AWeDB6AYm0yU3WvgN1IATx0EEBKUe3w==}
peerDependencies:
typescript: '>=5.0.4'
@ -2479,11 +2497,11 @@ packages:
optional: true
dependencies:
'@wagmi/chains': 1.6.0(typescript@5.1.6)
'@wagmi/connectors': 2.6.6(@wagmi/chains@1.6.0)(react@18.2.0)(typescript@5.1.6)(viem@1.4.1)
abitype: 0.8.7(typescript@5.1.6)
'@wagmi/connectors': 2.6.6(@wagmi/chains@1.6.0)(react@18.2.0)(typescript@5.1.6)(viem@1.4.1)(zod@3.21.4)
abitype: 0.8.7(typescript@5.1.6)(zod@3.21.4)
eventemitter3: 4.0.7
typescript: 5.1.6
viem: 1.4.1(typescript@5.1.6)
viem: 1.4.1(typescript@5.1.6)(zod@3.21.4)
zustand: 4.3.9(react@18.2.0)
transitivePeerDependencies:
- '@react-native-async-storage/async-storage'
@ -3250,7 +3268,7 @@ packages:
through: 2.3.8
dev: false
/abitype@0.8.7(typescript@5.1.6):
/abitype@0.8.7(typescript@5.1.6)(zod@3.21.4):
resolution: {integrity: sha512-wQ7hV8Yg/yKmGyFpqrNZufCxbszDe5es4AZGYPBitocfSqXtjrTG9JMWFcc4N30ukl2ve48aBTwt7NJxVQdU3w==}
peerDependencies:
typescript: '>=5.0.4'
@ -3260,9 +3278,10 @@ packages:
optional: true
dependencies:
typescript: 5.1.6
zod: 3.21.4
dev: false
/abitype@0.9.3(typescript@5.1.6):
/abitype@0.9.3(typescript@5.1.6)(zod@3.21.4):
resolution: {integrity: sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w==}
peerDependencies:
typescript: '>=5.0.4'
@ -3274,6 +3293,7 @@ packages:
optional: true
dependencies:
typescript: 5.1.6
zod: 3.21.4
dev: false
/acorn-jsx@5.3.2(acorn@8.10.0):
@ -5281,6 +5301,11 @@ packages:
engines: {node: '>=4'}
dev: true
/mini-svg-data-uri@1.4.4:
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
hasBin: true
dev: true
/minimalistic-assert@1.0.1:
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
dev: false
@ -6540,6 +6565,18 @@ packages:
magic-string: 0.30.1
periscopic: 3.1.0
/sveltekit-superforms@1.5.0(@sveltejs/kit@1.22.3)(svelte@4.1.1)(zod@3.21.4):
resolution: {integrity: sha512-c/2v6zN9rtIAsOfsd6rBV7Kf0lB2+/o6sAFaOuMrYPlTBro5lpbz3iN/3nM5D3EuLE2Xt/iDdZYpkQdH8rq4nQ==}
peerDependencies:
'@sveltejs/kit': 1.x
svelte: 3.x || 4.x
zod: 3.x
dependencies:
'@sveltejs/kit': 1.22.3(svelte@4.1.1)(vite@4.4.7)
svelte: 4.1.1
zod: 3.21.4
dev: true
/symbol-observable@2.0.3:
resolution: {integrity: sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==}
engines: {node: '>=0.10'}
@ -6841,7 +6878,7 @@ packages:
resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==}
dev: false
/viem@1.4.1(typescript@5.1.6):
/viem@1.4.1(typescript@5.1.6)(zod@3.21.4):
resolution: {integrity: sha512-MtaoBHDSJDqa+QyXKG5d+S6EQSebRO0tzw6anSP4zC7AbC614vMeg9Y8LbkmEkWCw8swFYkort+H9l7GkWB0uA==}
peerDependencies:
typescript: '>=5.0.4'
@ -6855,7 +6892,7 @@ packages:
'@scure/bip32': 1.3.0
'@scure/bip39': 1.2.0
'@wagmi/chains': 1.6.0(typescript@5.1.6)
abitype: 0.9.3(typescript@5.1.6)
abitype: 0.9.3(typescript@5.1.6)(zod@3.21.4)
isomorphic-ws: 5.0.0(ws@8.12.0)
typescript: 5.1.6
ws: 8.12.0
@ -7175,6 +7212,9 @@ packages:
engines: {node: '>=12.20'}
dev: true
/zod@3.21.4:
resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
/zustand@4.3.9(react@18.2.0):
resolution: {integrity: sha512-Tat5r8jOMG1Vcsj8uldMyqYKC5IZvQif8zetmLHs9WoZlntTHmIoNM8TpLRY31ExncuUvUOXehd0kvahkuHjDw==}
engines: {node: '>=12.7.0'}

View File

@ -0,0 +1,81 @@
<script>
import { Stepper, Step } from '@skeletonlabs/skeleton';
import { form, field } from 'svelte-forms';
import { required, between } from 'svelte-forms/validators';
let step = 1;
let name = '';
let age = '';
let flowData = {};
const handleNextStep = () => {
if (step === 1 && name.trim() === '') {
alert('Please enter a valid name.');
return;
}
if (step === 2 && age.trim() === '') {
alert('Please enter a valid age.');
return;
}
step += 1;
};
const handlePrevStep = () => {
step -= 1;
};
const handleConfirm = () => {
flowData = {
name,
age
};
alert('Flow data confirmed: ' + JSON.stringify(flowData));
};
const handleReset = () => {
step = 1;
name = '';
age = '';
flowData = {};
};
</script>
<Stepper start={step - 1}>
<Step>
<svelte:fragment slot="header">Step 1: Name</svelte:fragment>
<div>
<label for="name">Name:</label>
<input class="text-black" type="text" id="name" bind:value={name} />
</div>
<div slot="navigation">
<button class="btn variant-ghost" on:click={handleNextStep}>Next</button>
</div>
</Step>
<Step>
<svelte:fragment slot="header">Step 2: Age</svelte:fragment>
<div>
<label for="age">Age:</label>
<input class="text-black" type="number" id="age" bind:value={age} />
</div>
<div slot="navigation">
<button class="btn variant-ghost" on:click={handlePrevStep}>Previous</button>
<button class="btn variant-ghost" on:click={handleNextStep}>Next</button>
</div>
</Step>
<Step>
<svelte:fragment slot="header">Step 3: Confirm Summary</svelte:fragment>
<div>
<h3>Confirm Summary:</h3>
<p>Name: {name}</p>
<p>Age: {age}</p>
</div>
<div slot="navigation">
<button class="btn variant-ghost" on:click={handlePrevStep}>Previous</button>
<button class="btn variant-ghost-primary" on:click={handleConfirm}>Confirm</button>
</div>
</Step>
</Stepper>
<button on:click={handleReset}>Reset</button>

View File

@ -0,0 +1,5 @@
import { z } from 'zod';
export const UserSchema = z.object({
name: z.string().min(3).max(10)
});

View File

@ -11,9 +11,10 @@
},
layout: {
areas: `
"main"
"footer";
"main aside"
"footer footer";
`,
columns: '1fr 1fr',
rows: '1fr auto'
},
children: [
@ -34,6 +35,11 @@
},
services: ['helloEarthAlert']
},
{
id: 'testflows',
component: 'Flows',
slot: 'aside'
},
{
id: 'terminal',
component: 'Terminal',

View File

@ -0,0 +1,56 @@
<script lang="ts">
import { superForm } from 'sveltekit-superforms/client';
import { UserSchema } from '$lib/types/UserSchema';
const initialFormData = { name: '' };
const { form, errors, validate, constraints } = superForm(initialFormData, {
validators: UserSchema,
warnings: {
noValidationAndConstraints: false
},
validationMethod: 'oninput' // Trigger validation on input events
});
async function handleSubmit() {
// Manually validate the form
const validationResult = await validate();
// Prevent submission if there are errors
if (!validationResult.valid) {
return;
}
// Here, we'll just log the form data
console.log(form);
}
</script>
<div class="flex items-center justify-center min-h-screen">
<form on:submit|preventDefault={handleSubmit} class="w-full max-w-md">
<div class="mb-4">
{#if $errors.name}
<span class="block mb-2 font-semibold text-red-500">{$errors.name}</span>
{:else}
<label for="name" class="block mb-2 font-semibold text-white">Name</label>
{/if}
<input
name="name"
type="text"
class="w-full px-3 py-2 bg-transparent border-gray-100 rounded-md border-1 ring-0 ring-white focus:outline-none focus:ring-2 focus:ring-blue-500"
bind:value={$form.name}
aria-invalid={$errors.name ? 'true' : undefined}
{...constraints.name}
/>
</div>
<button
type="submit"
class="w-full px-4 py-2 text-white bg-blue-500 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={$errors.name}
>
Submit
</button>
</form>
</div>

View File

@ -16,6 +16,7 @@ module.exports = {
},
plugins: [
// 3. Append the Skeleton plugin to the end of this list
require('@tailwindcss/forms'),
...require('@skeletonlabs/skeleton/tailwind/skeleton.cjs')()
]
}