Added Basic xstate store management POC

This commit is contained in:
Samuel Andert
2023-08-01 11:58:34 +02:00
parent 3d2960390a
commit 125d7d997e
10 changed files with 224 additions and 113 deletions

@ -1,4 +1,4 @@
<script>
<script lang="ts">
import { fetchBalance } from '@wagmi/core';
import { onMount } from 'svelte';

@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import { connectWallet } from '$lib/services/wallet/wallet';
import WalletConnect from '$lib/WalletConnect.svelte';
import Send from '$lib/Send.svelte';
// export let id;
export let store;
@ -21,6 +22,7 @@
<div class="mb-4 text-lg font-medium">
PKP Wallet: <span class="text-blue-600">{$store.pkpWallet.address}</span>
</div>
<Send pkpWallet={$store.pkpWallet} />
{/if}
<WalletConnect />
</div>

@ -0,0 +1,14 @@
<!-- src/lib/components/CheckValidation.svelte -->
<script lang="ts">
export let store;
$: console.log('checkvalidation: ' + $store.isValidated);
</script>
<div class="flex items-center justify-center w-12 h-12 m-10 rounded-full">
{#if $store.isValidated}
<div class="w-full h-full bg-green-500 rounded-full" />
{:else}
<div class="w-full h-full bg-red-500 rounded-full" />
{/if}
</div>

@ -1,10 +1,16 @@
<script lang="ts">
import { superForm, message } from 'sveltekit-superforms/client';
import { superForm } from 'sveltekit-superforms/client';
import { UserSchema } from '$lib/types/UserSchema';
import { afterUpdate } from 'svelte';
import { writable } from 'svelte/store';
import { RangeSlider, SlideToggle } from '@skeletonlabs/skeleton';
export let store;
export let services;
let isStoreLoaded = false;
$: if ($store) isStoreLoaded = true;
const initialFormData = {
name: '',
email: '',
@ -17,19 +23,42 @@
const fields = ['name', 'email', 'about', 'age', 'favoriteFood', 'slider', 'toggle'];
const { form, errors, validate, constraints, capture, restore } = superForm(initialFormData, {
const { form, errors, validate, constraints } = superForm(initialFormData, {
validators: UserSchema,
warnings: {
noValidationAndConstraints: false
},
validationMethod: 'oninput', // Trigger validation on input events
// Set the clearOnSubmit option to clear both errors and message on submit
clearOnSubmit: 'errors-and-message'
});
export const snapshot = { capture, restore };
const successMessage = writable<string | null>(null);
// Update the isValidated property of the store whenever the errors object changes
$: {
$store.isValidated = !(
$errors.name ||
$errors.email ||
$errors.about ||
$errors.age ||
$errors.favoriteFood
);
}
$: {
if (
!$errors.name &&
!$errors.email &&
!$errors.about &&
!$errors.age &&
!$errors.favoriteFood
) {
services.validationRecipe.validateMe().send('VALIDATE');
} else {
services.validationRecipe.validateMe().send('INVALIDATE');
}
}
async function handleSubmit() {
// Manually validate the form
const validationResult = await validate();
@ -61,7 +90,10 @@
});
</script>
{#if isStoreLoaded}
<div class="flex items-center justify-center min-h-screen overflow-scroll">
<div class="w-full">
STORE: {JSON.stringify($store)}
{#if $successMessage}
<!-- Display the success message instead of the form -->
<aside class="w-full max-w-md p-4 alert variant-ghost" id="message-container">
@ -90,8 +122,8 @@
<span class="block mb-2 font-semibold text-red-500">{$errors[field]}</span>
{:else}
<label for={field} class="block mb-2 font-semibold text-white"
>{field.charAt(0).toUpperCase() + field.slice(1)}</label
>
>{field.charAt(0).toUpperCase() + field.slice(1)}
</label>
{/if}
{#if field === 'about'}
@ -127,7 +159,14 @@
{...constraints[field]}
/>
{:else if field === 'slider'}
<RangeSlider name={field} bind:value={$form[field]} min={0} max={100} step={1} ticked>
<RangeSlider
name={field}
bind:value={$form[field]}
min={0}
max={100}
step={1}
ticked
>
<div class="flex items-center justify-between">
<div class="text-xs">{$form[field]} / 100</div>
</div>
@ -146,18 +185,15 @@
{/if}
</div>
{/each}
<button
type="submit"
class="w-full px-4 py-2 mt-4 text-white bg-blue-500 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={$errors.name ||
$errors.email ||
$errors.about ||
$errors.age ||
$errors.favoriteFood}
disabled={!$store.isValidated}
>
Submit
</button>
</form>
{/if}
</div>
</div>
{/if}

@ -1,8 +0,0 @@
/src/lib/components/examples/Form.svelte:98:7 'type' attribute cannot be dynamic if input uses two-way binding
/src/lib/components/examples/Form.svelte:98:7
96 | <input
97 | name={field}
98 | type={field === 'age' ? 'number' : 'text'}
^
99 | 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"
100 | bind:value={$form[field]}

@ -0,0 +1 @@
{"core":{},"composite.Interpreter2":{"id":"validation"},"helloEarthAlert":{}}

@ -0,0 +1,27 @@
import { createMachine, interpret } from 'xstate';
const validationMachine = createMachine({
id: 'validation',
initial: 'notValidated',
states: {
notValidated: {
on: {
VALIDATE: 'validated'
}
},
validated: {
on: {
INVALIDATE: 'notValidated'
}
}
}
});
export function validateMe() {
const service = interpret(validationMachine).onTransition((state) => {
console.log('Current validation state:', state.value);
}).start();
return service;
}

@ -29,7 +29,7 @@ const validationMessages = {
};
export const UserSchema = z.object({
name: z.string().min(3, validationMessages.name.minLength).max(10, validationMessages.name.maxLength),
name: z.string().nonempty('Name is required.').min(3, validationMessages.name.minLength).max(10, validationMessages.name.maxLength),
email: z.string().email(validationMessages.email.isEmail),
about: z.string().max(500, validationMessages.about.maxLength),
age: z.number().min(18, validationMessages.age.min).max(120, validationMessages.age.max),

@ -0,0 +1,39 @@
<!-- src/routes/form/+page.svelte -->
<script lang="ts">
import Composite from '$lib/core/Composite.svelte';
let composite = {
id: 'testform',
store: {
isValidated: false
},
layout: {
areas: `
"checkvalidation form"
`,
columns: '1fr 3fr',
rows: 'auto'
},
children: [
{
id: 'checkvalidation',
component: 'CheckValidation',
slot: 'checkvalidation',
map: {
isValidated: '@testform:isValidated'
}
},
{
id: 'form',
component: 'Form',
slot: 'form',
map: {
isValidated: '@testform:isValidated'
},
services: ['validationRecipe', 'helloEarthAlert']
}
]
};
</script>
<Composite {composite} />

@ -6,7 +6,7 @@
store: {
title: 'Hello Earth',
description:
'Here you can find all the references, how to use the store and mapping logic of store to store and data to store maps',
'how to use the store and mapping logic of store to store and data to store maps',
helloMapMe: 'this is going to be mapped'
},
layout: {