190 lines
5.3 KiB
Svelte
190 lines
5.3 KiB
Svelte
<script lang="ts">
|
|
import { createMachine, assign } from 'xstate';
|
|
import { useMachine } from '@xstate/svelte';
|
|
import { superForm } from 'sveltekit-superforms/client';
|
|
import TextInput from './inputFields/TextInput.svelte';
|
|
import ToggleInput from './inputFields/ToggleInput.svelte';
|
|
import SliderInput from './inputFields/SliderInput.svelte';
|
|
import SelectInput from './inputFields/SelectInput.svelte';
|
|
import TextAreaInput from './inputFields/TextAreaInput.svelte';
|
|
import NumberInput from './inputFields/NumberInput.svelte';
|
|
|
|
export let me;
|
|
|
|
const { fields, validators } = $me.context;
|
|
|
|
const initialFormData = fields.reduce((acc, field) => {
|
|
acc[field.name] = field.placeholder;
|
|
return acc;
|
|
}, {});
|
|
|
|
const { form, errors, validate, constraints } = superForm(initialFormData, {
|
|
validators: validators,
|
|
warnings: {
|
|
noValidationAndConstraints: false
|
|
},
|
|
validationMethod: 'oninput',
|
|
clearOnSubmit: 'errors-and-message'
|
|
});
|
|
|
|
const formMachine = createMachine({
|
|
id: 'form',
|
|
initial: 'input',
|
|
context: {
|
|
currentField: 0,
|
|
formData: initialFormData
|
|
},
|
|
states: {
|
|
input: {
|
|
on: {
|
|
NEXT: [
|
|
{
|
|
target: 'submitting',
|
|
actions: assign({
|
|
formData: (context, event) => ({
|
|
...context.formData,
|
|
[fields[context.currentField].name]: event.fieldValue
|
|
})
|
|
}),
|
|
cond: (context) => context.currentField === fields.length - 1
|
|
},
|
|
{
|
|
target: 'input',
|
|
actions: [
|
|
assign({
|
|
currentField: (context) => context.currentField + 1,
|
|
formData: (context, event) => ({
|
|
...context.formData,
|
|
[fields[context.currentField].name]: event.fieldValue
|
|
})
|
|
})
|
|
]
|
|
}
|
|
],
|
|
PREV: {
|
|
target: 'input',
|
|
actions: assign({
|
|
currentField: (context) => context.currentField - 1
|
|
}),
|
|
cond: (context) => context.currentField > 0
|
|
}
|
|
}
|
|
},
|
|
submitting: {
|
|
on: {
|
|
SUBMIT: 'submitted'
|
|
}
|
|
},
|
|
submitted: {
|
|
// Add your submission logic here
|
|
}
|
|
}
|
|
});
|
|
|
|
const { state, send } = useMachine(formMachine);
|
|
|
|
async function handleNext() {
|
|
const currentFieldName = fields[$state.context.currentField].name;
|
|
const validationResult = await validate(currentFieldName);
|
|
|
|
if (validationResult && !validationResult.valid) {
|
|
return;
|
|
}
|
|
|
|
const fieldValue = $form[currentFieldName];
|
|
console.log(`Current input value: ${fieldValue}`);
|
|
send('NEXT', { fieldValue });
|
|
|
|
// Log the context formData
|
|
console.log(`Context formData: ${JSON.stringify($state.context.formData)}`);
|
|
}
|
|
|
|
function handleKeyDown(event) {
|
|
if (event.key === 'Enter') {
|
|
event.preventDefault();
|
|
handleNext();
|
|
}
|
|
}
|
|
let childInput;
|
|
$: {
|
|
childInput = {
|
|
form,
|
|
errors,
|
|
validate,
|
|
field: fields[$state.context.currentField],
|
|
constraints
|
|
};
|
|
}
|
|
</script>
|
|
|
|
<div class="p-6">
|
|
<form on:submit|preventDefault={handleNext} on:keydown={handleKeyDown} class="w-full max-w-md">
|
|
<div class="mb-4">
|
|
{#if $errors[fields[$state.context.currentField].name]}
|
|
<span class="block mb-2 font-semibold text-red-500">
|
|
{$errors[fields[$state.context.currentField].name]}
|
|
</span>
|
|
{:else}
|
|
<label
|
|
for={fields[$state.context.currentField].name}
|
|
class="block mb-2 font-semibold text-white"
|
|
>
|
|
{fields[$state.context.currentField].name.charAt(0).toUpperCase() +
|
|
fields[$state.context.currentField].name.slice(1)}
|
|
</label>
|
|
{/if}
|
|
{#if fields[$state.context.currentField].type === 'text'}
|
|
<TextInput {childInput} />
|
|
{:else if fields[$state.context.currentField].type === 'email'}
|
|
<TextInput {childInput} />
|
|
{:else if fields[$state.context.currentField].type === 'textarea'}
|
|
<TextAreaInput {childInput} />
|
|
{:else if fields[$state.context.currentField].type === 'select'}
|
|
<SelectInput {childInput} />
|
|
{:else if fields[$state.context.currentField].type === 'slider'}
|
|
<SliderInput {childInput} />
|
|
{:else if fields[$state.context.currentField].type === 'toggle'}
|
|
<ToggleInput {childInput} />
|
|
{:else if fields[$state.context.currentField].type === 'number'}
|
|
<NumberInput {childInput} />
|
|
{/if}
|
|
</div>
|
|
{#if $state.matches('submitting')}
|
|
<div class="mb-4">
|
|
<h2 class="text-lg font-semibold text-white">Summary</h2>
|
|
{#each Object.entries($state.context.formData) as [key, value]}
|
|
<p class="mt-1 text-white">{key}: {value}</p>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
<div class="flex justify-between mt-4">
|
|
<button
|
|
type="button"
|
|
on:click={() => send('PREV')}
|
|
class="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={$state.context.currentField === 0}
|
|
>
|
|
Previous
|
|
</button>
|
|
{#if !$state.matches('submitting')}
|
|
<button
|
|
type="button"
|
|
on:click={() => send('NEXT')}
|
|
class="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[fields[$state.context.currentField].name]}
|
|
>
|
|
Next
|
|
</button>
|
|
{/if}
|
|
{#if $state.matches('submitting')}
|
|
<button
|
|
type="submit"
|
|
class="px-4 py-2 text-white bg-green-500 rounded-md hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500"
|
|
>
|
|
Submit
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
</form>
|
|
</div>
|